Added the files.
This commit is contained in:
commit
38ccdcbfe5
124 changed files with 32079 additions and 0 deletions
25
client/.gitignore
vendored
Normal file
25
client/.gitignore
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
firebase-debug.log*
|
16
client/README.md
Normal file
16
client/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# HMS client-side code
|
||||
|
||||
## Get Started
|
||||
|
||||
Get started developing...
|
||||
|
||||
```shell
|
||||
# install deps
|
||||
npm install
|
||||
|
||||
# run in development mode
|
||||
npm run dev
|
||||
|
||||
# run tests
|
||||
npm run test
|
||||
```
|
11
client/continuous_deployment/before_install.sh
Normal file
11
client/continuous_deployment/before_install.sh
Normal file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
set -ev
|
||||
#run only on master
|
||||
if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then
|
||||
npm install -g firebase-tools
|
||||
npm install -g selenium-webdriver
|
||||
npm install codecov.io coveralls
|
||||
cd functions
|
||||
npm install
|
||||
cd ..
|
||||
fi
|
88
client/continuous_deployment/bs.js
Normal file
88
client/continuous_deployment/bs.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
// To run this script use this command
|
||||
// node bs.js yourBSUserName yourBSKey
|
||||
|
||||
var webdriver = require('selenium-webdriver')
|
||||
var test = require('./bs_test.js')
|
||||
|
||||
// Input capabilities
|
||||
var iPhone = {
|
||||
browserName: 'iPhone',
|
||||
device: 'iPhone 7',
|
||||
realMobile: 'true',
|
||||
os_version: '10.3',
|
||||
'browserstack.user': process.argv[2],
|
||||
'browserstack.key': process.argv[3],
|
||||
}
|
||||
|
||||
var android = {
|
||||
browserName: 'android',
|
||||
device: 'Samsung Galaxy S8',
|
||||
realMobile: 'true',
|
||||
os_version: '7.0',
|
||||
'browserstack.user': process.argv[2],
|
||||
'browserstack.key': process.argv[3],
|
||||
}
|
||||
|
||||
var desktopFF = {
|
||||
browserName: 'Firefox',
|
||||
browser_version: '59.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
resolution: '1024x768',
|
||||
'browserstack.user': process.argv[2],
|
||||
'browserstack.key': process.argv[3],
|
||||
}
|
||||
|
||||
var desktopEdge = {
|
||||
browserName: 'Edge',
|
||||
browser_version: '16.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
resolution: '1024x768',
|
||||
'browserstack.user': process.argv[2],
|
||||
'browserstack.key': process.argv[3],
|
||||
}
|
||||
|
||||
var desktopIE = {
|
||||
browserName: 'Chrome',
|
||||
browser_version: '69.0',
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
resolution: '1024x768',
|
||||
'browserstack.user': process.argv[2],
|
||||
'browserstack.key': process.argv[3],
|
||||
}
|
||||
|
||||
/*
|
||||
var iPhoneDriver = new webdriver.Builder()
|
||||
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
|
||||
.withCapabilities(iPhone)
|
||||
.build()
|
||||
|
||||
|
||||
var androidDriver = new webdriver.Builder()
|
||||
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
|
||||
.withCapabilities(android)
|
||||
.build()
|
||||
*/
|
||||
|
||||
var desktopFFDriver = new webdriver.Builder()
|
||||
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
|
||||
.withCapabilities(desktopFF)
|
||||
.build()
|
||||
|
||||
var desktopEdgeDriver = new webdriver.Builder()
|
||||
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
|
||||
.withCapabilities(desktopEdge)
|
||||
.build()
|
||||
|
||||
var desktopIEDriver = new webdriver.Builder()
|
||||
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
|
||||
.withCapabilities(desktopIE)
|
||||
.build()
|
||||
|
||||
//test.runTest(iPhoneDriver)
|
||||
//test.runTest(androidDriver)
|
||||
test.runTest(desktopFFDriver)
|
||||
test.runTest(desktopEdgeDriver)
|
||||
test.runTest(desktopIEDriver)
|
62
client/continuous_deployment/bs_test.js
Normal file
62
client/continuous_deployment/bs_test.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
var webdriver = require('selenium-webdriver')
|
||||
|
||||
const sleep = ms => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
module.exports.runTest = async driver => {
|
||||
try {
|
||||
await driver.get('https://www.react-most-wanted.com')
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
var signInButton = driver.wait(webdriver.until.elementLocated(webdriver.By.name('signin')))
|
||||
|
||||
await signInButton.click()
|
||||
await sleep(2000)
|
||||
|
||||
var passwordButton = driver.wait(
|
||||
webdriver.until.elementLocated(
|
||||
webdriver.By.className(
|
||||
'firebaseui-idp-button mdl-button mdl-js-button mdl-button--raised firebaseui-idp-password firebaseui-id-idp-button'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
await passwordButton.click()
|
||||
await sleep(2000)
|
||||
|
||||
var emailInput = driver.wait(webdriver.until.elementLocated(webdriver.By.name('email')))
|
||||
|
||||
await emailInput.sendKeys('test@test.com')
|
||||
await sleep(2000)
|
||||
|
||||
var nextButton = driver.wait(
|
||||
webdriver.until.elementLocated(
|
||||
webdriver.By.className(
|
||||
'firebaseui-id-submit firebaseui-button mdl-button mdl-js-button mdl-button--raised mdl-button--colored'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
await nextButton.click()
|
||||
await sleep(2000)
|
||||
|
||||
var passwordInput = driver.wait(webdriver.until.elementLocated(webdriver.By.name('password')))
|
||||
|
||||
await passwordInput.sendKeys('123456')
|
||||
await sleep(2000)
|
||||
|
||||
await passwordInput.sendKeys(webdriver.Key.ENTER)
|
||||
await sleep(2000)
|
||||
|
||||
driver.quit()
|
||||
} catch (e) {
|
||||
console.log('Test Failed')
|
||||
console.error(e)
|
||||
process.exitCode = 1
|
||||
process.abort()
|
||||
|
||||
driver.quit()
|
||||
}
|
||||
}
|
7
client/continuous_deployment/build.sh
Normal file
7
client/continuous_deployment/build.sh
Normal file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ev
|
||||
#run only on master
|
||||
if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then
|
||||
npm run build
|
||||
fi
|
8
client/continuous_deployment/deploy.sh
Normal file
8
client/continuous_deployment/deploy.sh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
set -ev
|
||||
#run only on master
|
||||
if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then
|
||||
firebase use prod --token $FIREBASE_TOKEN
|
||||
firebase deploy --non-interactive --token $FIREBASE_TOKEN
|
||||
node ./continuous_deployment/bs.js $BSNAME $BSKEY
|
||||
fi
|
6
client/jsconfig.json
Normal file
6
client/jsconfig.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
17632
client/package-lock.json
generated
Normal file
17632
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
57
client/package.json
Normal file
57
client/package.json
Normal file
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formatjs/intl-relativetimeformat": "7.x",
|
||||
"@material-ui/core": "4.x",
|
||||
"@material-ui/icons": "4.x",
|
||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||
"base-shell": "1.x",
|
||||
"crypto-js": "^4.0.0",
|
||||
"github-markdown-css": "4.x",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"intl": "1.x",
|
||||
"intl-locales-supported": "1.x",
|
||||
"material-ui-shell": "1.x",
|
||||
"notistack": "1.x",
|
||||
"react": "17.x",
|
||||
"react-custom-scrollbars": "4.x",
|
||||
"react-dom": "17.x",
|
||||
"react-intl": "5.x",
|
||||
"react-ios-pwa-prompt": "1.x",
|
||||
"react-markdown": "5.x",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router-dom": "5.x",
|
||||
"react-router-redux": "^4.0.8",
|
||||
"react-scripts": "4.0.0",
|
||||
"react-virtualized-auto-sizer": "1.x",
|
||||
"react-window": "1.x",
|
||||
"redux": "^4.0.5",
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
BIN
client/public/favicon.ico
Normal file
BIN
client/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
43
client/public/index.html
Normal file
43
client/public/index.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>HealthGeeks HMS-PMS</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
BIN
client/public/logo192.png
Normal file
BIN
client/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
client/public/logo512.png
Normal file
BIN
client/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
client/public/manifest.json
Normal file
25
client/public/manifest.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
3
client/public/robots.txt
Normal file
3
client/public/robots.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
13
client/src/App.js
Normal file
13
client/src/App.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React, { Component } from 'react'
|
||||
import App from 'base-shell/lib'
|
||||
import MUIConfig from 'material-ui-shell/lib'
|
||||
import merge from 'base-shell/lib/utils/config'
|
||||
import _config from './config'
|
||||
|
||||
const config = merge(MUIConfig, _config)
|
||||
|
||||
export default class Demo extends Component {
|
||||
render() {
|
||||
return <App config={config} />
|
||||
}
|
||||
}
|
11
client/src/config/apiRoutes.js
Normal file
11
client/src/config/apiRoutes.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* eslint-disable import/no-anonymous-default-export */
|
||||
export default {
|
||||
API_URL: 'http://localhost:3001',
|
||||
user: {
|
||||
user: "/api/v1/staff",
|
||||
},
|
||||
auth: {
|
||||
register: "/api/v1/staff/register",
|
||||
login: "/api/v1/staff/login"
|
||||
},
|
||||
};
|
37
client/src/config/config.js
Normal file
37
client/src/config/config.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { lazy } from 'react'
|
||||
import locales from './locales'
|
||||
import apiRoutes from './apiRoutes'
|
||||
import routes from './routes'
|
||||
import getMenuItems from './menuItems'
|
||||
import themes from './themes'
|
||||
import parseLanguages from 'base-shell/lib/utils/locale'
|
||||
|
||||
const config = {
|
||||
auth: {
|
||||
signInURL: '/',
|
||||
},
|
||||
apiRoutes,
|
||||
routes,
|
||||
locale: {
|
||||
locales,
|
||||
defaultLocale: parseLanguages( 'en'),
|
||||
onError: (e) => {
|
||||
//console.warn(e)
|
||||
return
|
||||
},
|
||||
},
|
||||
menu: {
|
||||
getMenuItems,
|
||||
},
|
||||
theme: {
|
||||
themes,
|
||||
defaultThemeID: 'default',
|
||||
defaultType: 'light',
|
||||
},
|
||||
pages: {
|
||||
LandingPage: lazy(() => import('../pages/LandingPage/LandingPage')),
|
||||
PageNotFound: lazy(() => import('../pages/PageNotFound/PageNotFound')),
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
1
client/src/config/index.js
Normal file
1
client/src/config/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './config.js'
|
43
client/src/config/locales/de.js
Normal file
43
client/src/config/locales/de.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineMessages } from 'react-intl'
|
||||
|
||||
const messages = defineMessages({
|
||||
app_name: 'React Meist Gesucht',
|
||||
sign_in: 'Anmelden',
|
||||
sign_out: 'Abmelden',
|
||||
sign_up: 'Anmeldung',
|
||||
email: 'Email',
|
||||
username: 'Nutzername',
|
||||
password: 'Passwort',
|
||||
about: 'Über',
|
||||
home: 'Startseite',
|
||||
page_not_found: 'Seite nicht gefunden',
|
||||
settings: 'Einstellungen',
|
||||
theme: 'Thema',
|
||||
default: 'Standard',
|
||||
red: 'Rot',
|
||||
green: 'Grün',
|
||||
language: 'Sprache',
|
||||
en: 'Englisch',
|
||||
de: 'Deutsch',
|
||||
ru: 'Russisch',
|
||||
menu: 'Menü',
|
||||
menu_mini_mode: 'Mini-Menü',
|
||||
offline: 'Offline',
|
||||
demos:'Demos',
|
||||
dialog_demo:'Demo Dialog',
|
||||
dialog_title:'Dialog titel',
|
||||
dialog_action:'JA, Löschen',
|
||||
dialog_message:`Dialognachricht. Sie können hier so viel Text einfügen, wie Sie möchten.
|
||||
Stellen Sie eine Frage oder zeigen Sie eine Warnung an, bevor Sie etwas löschen.
|
||||
Sie können den Aktionstext auch auf "JA, Löschen" setzen und diese Aktion ausführen, indem Sie eine "handleAction" -Stütze übergeben.
|
||||
Dies erhält einen "handleClose" -Rückruf, mit dem Sie den Dialog schließen können, wenn Ihre Aktion abgeschlossen ist.`,
|
||||
toast_demo:'Demo Toast',
|
||||
filter_demo:'Demo filter',
|
||||
list_page_demo:'List Page Demo mit {count} Zeilen',
|
||||
forgot_password:'Vergessen passwort',
|
||||
password_reset:'Passwort zurücksetzen',
|
||||
password_confirm:'Passwortbestätigung',
|
||||
registration:'Registrierung'
|
||||
})
|
||||
|
||||
export default messages
|
43
client/src/config/locales/en.js
Normal file
43
client/src/config/locales/en.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineMessages } from 'react-intl'
|
||||
|
||||
const messages = defineMessages({
|
||||
app_name: 'React Most Wanted',
|
||||
sign_in: 'Sign in',
|
||||
sign_out: 'Sign out',
|
||||
sign_up: 'Sign up',
|
||||
email: 'Email',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
about: 'About',
|
||||
home: 'Home',
|
||||
page_not_found: 'Page not found',
|
||||
settings: 'Settings',
|
||||
theme: 'Theme',
|
||||
default: 'Default',
|
||||
red: 'Red',
|
||||
green: 'Green',
|
||||
language: 'Language',
|
||||
en: 'English',
|
||||
de: 'German',
|
||||
ru: 'Russian',
|
||||
menu: 'Menu',
|
||||
menu_mini_mode: 'Mini menu',
|
||||
offline: 'Offline',
|
||||
demos:'Demos',
|
||||
dialog_demo:'Demo dialog',
|
||||
dialog_title:'Dialog title',
|
||||
dialog_action:'YES, Delete',
|
||||
dialog_message:`Dialog message. You can put as much text as you want here.
|
||||
Ask a question or show a warning befor deleting something.
|
||||
You can also set the action text to somerhing like "YES, Delete" and run that action by passing a "handleAction" prop.
|
||||
This receives a "handleClose" callback with wich you can close the dialog when your action is done.`,
|
||||
toast_demo:'Demo toast',
|
||||
filter_demo:'Demo filter',
|
||||
list_page_demo:'List Page demo with {count} rows',
|
||||
forgot_password:'Forgot password',
|
||||
password_reset:'Password reset',
|
||||
password_confirm:'Password confirm',
|
||||
registration:'Registration'
|
||||
})
|
||||
|
||||
export default messages
|
19
client/src/config/locales/index.js
Normal file
19
client/src/config/locales/index.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const locales = [
|
||||
{
|
||||
locale: 'en',
|
||||
messages: import('./en'),
|
||||
//loadData: import(`@formatjs/intl-relativetimeformat/dist/locale-data/en`),
|
||||
},
|
||||
{
|
||||
locale: 'ru',
|
||||
messages: import('./ru'),
|
||||
//loadData: import(`@formatjs/intl-relativetimeformat/dist/locale-data/ru`),
|
||||
},
|
||||
{
|
||||
locale: 'de',
|
||||
messages: import('./de'),
|
||||
//loadData: import(`@formatjs/intl-relativetimeformat/dist/locale-data/de`),
|
||||
},
|
||||
]
|
||||
|
||||
export default locales
|
43
client/src/config/locales/ru.js
Normal file
43
client/src/config/locales/ru.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineMessages } from 'react-intl'
|
||||
|
||||
const messages = defineMessages({
|
||||
app_name: 'React Most Wanted',
|
||||
sign_in: 'Вход',
|
||||
sign_out: 'Выход',
|
||||
sign_up: 'Зарегистрироваться',
|
||||
email: 'Эл. адрес',
|
||||
username: 'Имя пользователя',
|
||||
password: 'Пароль',
|
||||
about: 'О нас',
|
||||
home: 'Главная',
|
||||
page_not_found: 'Страница не найдена',
|
||||
settings: 'Настройки',
|
||||
theme: 'Тема',
|
||||
default: 'По умолчанию',
|
||||
red: 'Красная',
|
||||
green: 'Зелёная',
|
||||
language: 'Язык',
|
||||
en: 'English',
|
||||
de: 'Deutsch',
|
||||
ru: 'Русский',
|
||||
menu: 'Меню',
|
||||
menu_mini_mode: 'Мини меню',
|
||||
offline: 'Офлайн',
|
||||
demos:'Демонстрации',
|
||||
dialog_demo:'Демонстрация диалога',
|
||||
dialog_title:'Заголовок диалога',
|
||||
dialog_action:'Да, удалить',
|
||||
dialog_message:`Диалоговое сообщение. Вы можете поместить сюда сколько угодно текста.
|
||||
Задайте вопрос или покажите предупреждение перед удалением чего-либо.
|
||||
Вы также можете задать для текста действия значение somerhing, например «Да, Удалить», и запустить это действие, передав опору «handleAction».
|
||||
Он получает обратный вызов handleClose, с которым вы можете закрыть диалог, когда ваше действие будет выполнено.`,
|
||||
toast_demo:'Демонстрация toast',
|
||||
filter_demo:'Демонстрация фильтра',
|
||||
list_page_demo:'Демонстрация страницы списка с {count} строками',
|
||||
forgot_password:'Забыли пароль',
|
||||
password_reset:'Сброс пароля',
|
||||
password_confirm:'Подтвердить пароль',
|
||||
registration:'Регистрация',
|
||||
})
|
||||
|
||||
export default messages
|
149
client/src/config/menuItems.js
Normal file
149
client/src/config/menuItems.js
Normal file
|
@ -0,0 +1,149 @@
|
|||
import ChatBubble from '@material-ui/icons/ChatBubble'
|
||||
import DashboardIcon from '@material-ui/icons/Dashboard'
|
||||
import ExitToAppIcon from '@material-ui/icons/ExitToApp'
|
||||
import FilterList from '@material-ui/icons/FilterList'
|
||||
import GetApp from '@material-ui/icons/GetApp'
|
||||
import InfoOutlined from '@material-ui/icons/InfoOutlined'
|
||||
import LanguageIcon from '@material-ui/icons/Language'
|
||||
import LockIcon from '@material-ui/icons/Lock'
|
||||
import QuestionAnswer from '@material-ui/icons/QuestionAnswer'
|
||||
import React from 'react'
|
||||
import SettingsIcon from '@material-ui/icons/SettingsApplications'
|
||||
import StyleIcon from '@material-ui/icons/Style'
|
||||
import Tab from '@material-ui/icons/Tab'
|
||||
import ViewList from '@material-ui/icons/ViewList'
|
||||
import Web from '@material-ui/icons/Web'
|
||||
import allLocales from './locales'
|
||||
import allThemes from './themes'
|
||||
import { store, Action } from "reducers";
|
||||
|
||||
const getMenuItems = (props) => {
|
||||
const {
|
||||
intl,
|
||||
updateLocale,
|
||||
locale,
|
||||
menuContext,
|
||||
themeContext,
|
||||
a2HSContext,
|
||||
auth: authData,
|
||||
} = props
|
||||
const { isAuthMenuOpen, setMiniMode } = menuContext
|
||||
const { themeID, setThemeID } = themeContext
|
||||
const { setAuth } = authData
|
||||
const { isAppInstallable, isAppInstalled, deferredPrompt } = a2HSContext
|
||||
|
||||
const state = store.getState();
|
||||
let isAuthorised = state.Auth.get('authenticated');
|
||||
|
||||
store.subscribe(_ => {
|
||||
isAuthorised = store.getState().Auth.get('authenticated');
|
||||
isAuthorised && setAuth({ isAuthenticated: isAuthorised })
|
||||
})
|
||||
|
||||
setMiniMode(false);
|
||||
|
||||
const localeItems = allLocales.map((l) => {
|
||||
return {
|
||||
value: undefined,
|
||||
visible: true,
|
||||
primaryText: intl.formatMessage({ id: l.locale }),
|
||||
onClick: () => {
|
||||
updateLocale(l.locale)
|
||||
},
|
||||
leftIcon: <LanguageIcon />,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const themeItems = allThemes.map((t) => {
|
||||
return {
|
||||
value: undefined,
|
||||
visible: true,
|
||||
primaryText: intl.formatMessage({ id: t.id }),
|
||||
onClick: () => {
|
||||
setThemeID(t.id)
|
||||
},
|
||||
leftIcon: <StyleIcon style={{ color: t.color }} />,
|
||||
}
|
||||
})
|
||||
|
||||
if (isAuthMenuOpen || !isAuthorised) {
|
||||
return [
|
||||
{
|
||||
value: '/',
|
||||
onClick: isAuthorised
|
||||
? () => {
|
||||
setAuth({ isAuthenticated: false })
|
||||
store.dispatch(Action.LogoutUser())
|
||||
}
|
||||
: () => {},
|
||||
visible: true,
|
||||
primaryText: isAuthorised
|
||||
? intl.formatMessage({ id: 'sign_out' })
|
||||
: intl.formatMessage({ id: 'sign_in' }),
|
||||
leftIcon: isAuthorised ? <ExitToAppIcon /> : <LockIcon />,
|
||||
},
|
||||
]
|
||||
}
|
||||
return [
|
||||
{
|
||||
value: '/home',
|
||||
visible: isAuthorised,
|
||||
primaryText: intl.formatMessage({ id: 'home' }),
|
||||
leftIcon: <DashboardIcon />,
|
||||
},
|
||||
{
|
||||
value: '/staff_context',
|
||||
visible: true,
|
||||
primaryText: 'Staff Context',
|
||||
leftIcon: <Web />,
|
||||
},
|
||||
{
|
||||
value: '/patient_context',
|
||||
visible: true,
|
||||
primaryText: 'Patient Context',
|
||||
leftIcon: <Web />,
|
||||
},
|
||||
{
|
||||
value: '/division_context',
|
||||
visible: true,
|
||||
primaryText: 'Division Context',
|
||||
leftIcon: <Web />,
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
primaryText: intl.formatMessage({ id: 'settings' }),
|
||||
primaryTogglesNestedList: true,
|
||||
leftIcon: <SettingsIcon />,
|
||||
nestedItems: [
|
||||
{
|
||||
primaryText: intl.formatMessage({ id: 'theme' }),
|
||||
secondaryText: intl.formatMessage({ id: themeID }),
|
||||
primaryTogglesNestedList: true,
|
||||
leftIcon: <StyleIcon />,
|
||||
nestedItems: themeItems,
|
||||
},
|
||||
{
|
||||
primaryText: intl.formatMessage({ id: 'language' }),
|
||||
secondaryText: intl.formatMessage({ id: locale }),
|
||||
primaryTogglesNestedList: true,
|
||||
leftIcon: <LanguageIcon />,
|
||||
nestedItems: localeItems,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
value: null,
|
||||
visible: isAppInstallable && !isAppInstalled,
|
||||
onClick: () => {
|
||||
deferredPrompt.prompt()
|
||||
},
|
||||
primaryText: intl.formatMessage({
|
||||
id: 'install',
|
||||
defaultMessage: 'Install',
|
||||
}),
|
||||
leftIcon: <GetApp />,
|
||||
},
|
||||
]
|
||||
}
|
||||
export default getMenuItems
|
42
client/src/config/routes.js
Normal file
42
client/src/config/routes.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* eslint-disable react/jsx-key */
|
||||
import React, { lazy } from 'react'
|
||||
import AuthorizedRoute from 'base-shell/lib/components/AuthorizedRoute/AuthorizedRoute'
|
||||
import UnauthorizedRoute from 'base-shell/lib/components/UnauthorizedRoute/UnauthorizedRoute'
|
||||
import { Route } from 'react-router-dom'
|
||||
|
||||
const SignIn = lazy(() => import('../pages/SignIn/SignIn'))
|
||||
const SignUp = lazy(() => import('../pages/SignUp/SignUp'))
|
||||
const PasswordReset = lazy(() => import('../pages/PasswordReset/PasswordReset'))
|
||||
const About = lazy(() => import('../pages/About'))
|
||||
const Home = lazy(() => import('../pages/Home/Home'))
|
||||
const StaffContext = lazy(() => import('../pages/StaffContext/wrapper'))
|
||||
const PatientContext = lazy ( () => import('../pages/PatientContext'))
|
||||
const DivisionContext = lazy(() => import('../pages/DivisionContext'))
|
||||
|
||||
const routes = [
|
||||
<UnauthorizedRoute path="/signin" redirectTo="/" exact component={SignIn} />,
|
||||
<UnauthorizedRoute path="/auth/staff/login" redirectTo="/home" exact component={SignIn} />,
|
||||
<UnauthorizedRoute path="/auth/medical/login" redirectTo="/home" exact component={SignIn} />,
|
||||
<UnauthorizedRoute path="/auth/nurse/login" redirectTo="/home" exact component={SignIn} />,
|
||||
<UnauthorizedRoute path="/signup" redirectTo="/" exact component={SignUp} />,
|
||||
<UnauthorizedRoute path="/auth/staff/signup" redirectTo="/home" exact component={SignUp} />,
|
||||
<UnauthorizedRoute path="/auth/medical/signup" redirectTo="/home" exact component={SignUp} />,
|
||||
<UnauthorizedRoute path="/auth/nurse/signup" redirectTo="/home" exact component={SignUp} />,
|
||||
|
||||
<UnauthorizedRoute
|
||||
path="/password_reset"
|
||||
redirectTo="/"
|
||||
exact
|
||||
component={PasswordReset}
|
||||
/>,
|
||||
<Route path="/about" exact component={About} />,
|
||||
|
||||
|
||||
<AuthorizedRoute path="/home" exact component={Home} />,
|
||||
<AuthorizedRoute path="/patient_context" exact component={PatientContext} />,
|
||||
<AuthorizedRoute path="/staff_context" exact component={StaffContext} />,
|
||||
<AuthorizedRoute path="/division_context" exact component={DivisionContext} />,
|
||||
|
||||
]
|
||||
|
||||
export default routes
|
33
client/src/config/themes.js
Normal file
33
client/src/config/themes.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import red from '@material-ui/core/colors/red'
|
||||
import pink from '@material-ui/core/colors/pink'
|
||||
import green from '@material-ui/core/colors/green'
|
||||
|
||||
const themes = [
|
||||
{
|
||||
id: 'default',
|
||||
},
|
||||
{
|
||||
id: 'red',
|
||||
color: red[500],
|
||||
source: {
|
||||
palette: {
|
||||
primary: red,
|
||||
secondary: pink,
|
||||
error: red,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'green',
|
||||
color: green[500],
|
||||
source: {
|
||||
palette: {
|
||||
primary: green,
|
||||
secondary: red,
|
||||
error: red,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default themes
|
64
client/src/constants/index.js
Normal file
64
client/src/constants/index.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
//TODO: Break up large constants file into smaller constants file
|
||||
|
||||
const PUBLIC_URL = process.env.PUBLIC_URL;
|
||||
const PHOTO_NAMES = {
|
||||
cloud: `${PUBLIC_URL}/img/icons/cloud.png`,
|
||||
earth: `${PUBLIC_URL}/img/icons/earth.png`,
|
||||
heart: `${PUBLIC_URL}/img/icons/heart.png`,
|
||||
};
|
||||
|
||||
// Router's base (i.e. anything after the domain)
|
||||
const ROUTER_BASE_NAME = "/";
|
||||
|
||||
// Various Server URLs
|
||||
var SERVER_URL = "http://localhost:8081";
|
||||
if (process && process.env) {
|
||||
if (process.env.REACT_APP_SERVER_TYPE === "staging") {
|
||||
SERVER_URL = "https://backend-staging.herokuapp.com";
|
||||
}
|
||||
if (process.env.REACT_APP_SERVER_TYPE === "prod") {
|
||||
SERVER_URL = "https://backend-prod.herokuapp.com";
|
||||
}
|
||||
}
|
||||
|
||||
// Editor and Output constants
|
||||
//View Mode
|
||||
const CODE_AND_OUTPUT = 0;
|
||||
const CODE_ONLY = 1;
|
||||
const OUTPUT_ONLY = 2;
|
||||
|
||||
//UI
|
||||
|
||||
module.exports = {
|
||||
// photo names
|
||||
PHOTO_NAMES,
|
||||
DEFAULT_PHOTO_NAME: "icecream",
|
||||
|
||||
// Router Base Name
|
||||
ROUTER_BASE_NAME,
|
||||
|
||||
//Server Host Name
|
||||
SERVER_URL,
|
||||
|
||||
//User value constants
|
||||
MINIMUM_USERNAME_LENGTH: 6,
|
||||
MINIMUM_PASSWORD_LENGTH: 6,
|
||||
MINIMUM_DISPLAY_NAME_LENGTH: 1,
|
||||
MAXIMUM_USERNAME_LENGTH: 32,
|
||||
MAXIMUM_PASSWORD_LENGTH: 128,
|
||||
MAXIMUM_DISPLAY_NAME_LENGTH: 25,
|
||||
|
||||
// UI constants
|
||||
RING_LOADER_SIZE: 50,
|
||||
OPEN_PANEL_LEFT: 0,
|
||||
|
||||
// editor constants:
|
||||
CODE_AND_OUTPUT,
|
||||
CODE_ONLY,
|
||||
OUTPUT_ONLY,
|
||||
|
||||
// UI
|
||||
|
||||
//Firebase constants
|
||||
EMAIL_DOMAIN_NAME: "@fake.com",
|
||||
};
|
14
client/src/index.js
Normal file
14
client/src/index.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import { render } from "react-dom";
|
||||
import { createStore } from "redux";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
import App from "./App";
|
||||
import { store } from "reducers";
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById("root")
|
||||
);
|
6
client/src/lib/index.js
Normal file
6
client/src/lib/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import * as storage from "../lib/storage";
|
||||
|
||||
|
||||
export default {
|
||||
...storage,
|
||||
};
|
49
client/src/lib/storage.js
Normal file
49
client/src/lib/storage.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
const storageAvailable = (type) => {
|
||||
try {
|
||||
const storage = window[type];
|
||||
|
||||
|
||||
const x = '__storage_test__';
|
||||
storage.setItem(x, x);
|
||||
storage.removeItem(x);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class CookieStore {
|
||||
static set(key, value) {
|
||||
document.cookie = `${key}=${value}`;
|
||||
}
|
||||
|
||||
static get(key) {
|
||||
if (!document.cookie || document.cookie.length === 0) return undefined;
|
||||
const cookies = {};
|
||||
document.cookie.split(';').forEach((cookie) => {
|
||||
cookies[cookie.split('=')[0].trim()] = cookie.split('=')[1].trim();
|
||||
});
|
||||
return cookies[key];
|
||||
}
|
||||
|
||||
static remove(key) {
|
||||
document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
|
||||
}
|
||||
}
|
||||
|
||||
class LocalStore {
|
||||
static set(key, value) {
|
||||
window.localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
static get(key, value) {
|
||||
return window.localStorage.getItem(key);
|
||||
}
|
||||
|
||||
static remove(key) {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
export default (storageAvailable('localStorage') ? LocalStore : CookieStore);
|
||||
|
41
client/src/pages/About/index.js
Normal file
41
client/src/pages/About/index.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useIntl } from 'react-intl'
|
||||
import Page from 'material-ui-shell/lib/containers/Page'
|
||||
import Scrollbar from 'material-ui-shell/lib/components/Scrollbar'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import 'github-markdown-css'
|
||||
|
||||
export default function () {
|
||||
const [source, setSource] = useState(null)
|
||||
const intl = useIntl()
|
||||
|
||||
const loadData = async () => {
|
||||
const data = await fetch(
|
||||
'https://raw.githubusercontent.com/TarikHuber/react-most-wanted/master/README.md'
|
||||
)
|
||||
const text = await data.text()
|
||||
setSource(text)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Page
|
||||
pageTitle={intl.formatMessage({ id: 'about', defaultMessage: 'About' })}
|
||||
>
|
||||
<Scrollbar>
|
||||
<div style={{ backgroundColor: 'white', padding: 12 }}>
|
||||
{source && (
|
||||
<ReactMarkdown
|
||||
className="markdown-body"
|
||||
source={source}
|
||||
escapeHtml
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Scrollbar>
|
||||
</Page>
|
||||
)
|
||||
}
|
93
client/src/pages/DivisionContext/DivisionView.js
Normal file
93
client/src/pages/DivisionContext/DivisionView.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
import React from 'react';
|
||||
import Page from 'material-ui-shell/lib/containers/Page';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TableContainer from '@material-ui/core/TableContainer';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
});
|
||||
|
||||
function createDivision(id, name, location, numOfBeds, chargeNurse, ext, status) {
|
||||
return { id, name, location, numOfBeds, chargeNurse, ext, status };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createDivision(2421, 'Orthopedic', 'Bay 6', 6, "Ella", 2 , false),
|
||||
createDivision(1343, 'Emergency', 'Ward 6', 1, 'Johnson', 1, true),
|
||||
createDivision(5413, 'Psychiatric', 'Ward 11', 3, "Taggart", 5, true),
|
||||
createDivision(8193, 'Cardiac', 'Ward 4', 5, "Vaquez", 8, false),
|
||||
];
|
||||
|
||||
function DivisionView () {
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
React.useEffect ( () => {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
};
|
||||
|
||||
fetch('/', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
//we feed the data into the function to create division object
|
||||
//const dataRows = data.map((division) => {
|
||||
// return createDivision(division.id, division.name, division.location, division.numOfBeds, division.hargeNurse, division.ext, division.status);
|
||||
//})
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
})
|
||||
|
||||
return (
|
||||
<Page pageTitle='Division Context' >
|
||||
<div style={{padding: '32px'}}>
|
||||
<Typography variant="h4" style={{ marginBottom: "32px" }}> Division Vizualization</Typography>
|
||||
|
||||
<TableContainer component={Paper}>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>ID </TableCell>
|
||||
<TableCell> Name</TableCell>
|
||||
<TableCell>Location</TableCell>
|
||||
<TableCell>Num of Beds</TableCell>
|
||||
<TableCell> Charge Nurse </TableCell>
|
||||
<TableCell> Telephone Ext. </TableCell>
|
||||
<TableCell> Status </TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell>{row.id}</TableCell>
|
||||
<TableCell>{row.name}</TableCell>
|
||||
<TableCell>{row.location}</TableCell>
|
||||
<TableCell>{row.numOfBeds}</TableCell>
|
||||
<TableCell>{row.chargeNurse}</TableCell>
|
||||
<TableCell>{row.ext}</TableCell>
|
||||
<TableCell>{row.status ? "Complete" : "Incomplete"}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default DivisionView;
|
3
client/src/pages/DivisionContext/index.js
Normal file
3
client/src/pages/DivisionContext/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import DivisionView from './DivisionView';
|
||||
|
||||
export default DivisionView;
|
74
client/src/pages/Home/Home.js
Normal file
74
client/src/pages/Home/Home.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import HomeComponent from './HomeComponent';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Action } from 'reducers';
|
||||
|
||||
|
||||
class Home extends React.Component {
|
||||
saveChanges(newprofile) {
|
||||
this.props.updateUser(newprofile);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if (this.props.isAdmin) {
|
||||
return this.props.redirectHome();
|
||||
}
|
||||
if (this.props.authenticated) {
|
||||
this.props.fetchUser();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isAdmin) {
|
||||
return nextProps.redirectHome();
|
||||
}
|
||||
if (nextProps.updated) {
|
||||
setTimeout(() => {
|
||||
this.props.updateDone();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<HomeComponent
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const profile = {
|
||||
name: ''
|
||||
};
|
||||
|
||||
if (state.User.get('fetchSuccess')) {
|
||||
const User = state.User.get('profile');
|
||||
profile.name = `${User.firstName} ${User.lastName}`;
|
||||
}
|
||||
|
||||
return {
|
||||
profile,
|
||||
fetchSuccess: state.User.get('fetchSuccess'),
|
||||
updated: state.User.get('updated'),
|
||||
updateSuccess: state.User.get('updateSuccess'),
|
||||
updateError: state.User.get('error'),
|
||||
authenticated: state.Auth.get('authenticated'),
|
||||
isAdmin: state.Auth.get('isAdmin'),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchUser: () => {
|
||||
dispatch(Action.FetchUser());
|
||||
},
|
||||
updateUser: (newprofile) => {
|
||||
dispatch(Action.UpdateUser(newprofile));
|
||||
},
|
||||
updateDone: () => {
|
||||
dispatch(Action.UserUpdateDone());
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Home);
|
21
client/src/pages/Home/HomeComponent.js
Normal file
21
client/src/pages/Home/HomeComponent.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import Page from 'material-ui-shell/lib/containers/Page'
|
||||
import React from 'react'
|
||||
import Scrollbar from 'material-ui-shell/lib/components/Scrollbar/Scrollbar'
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
const HomePage = () => {
|
||||
const intl = useIntl()
|
||||
|
||||
const user = "Bob";
|
||||
|
||||
return (
|
||||
<Page pageTitle={'Welcome Bob'}>
|
||||
<Scrollbar
|
||||
style={{ height: '100%', width: '100%', display: 'flex', flex: 1 }}
|
||||
>
|
||||
Something here
|
||||
</Scrollbar>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
export default HomePage
|
77
client/src/pages/LandingPage/LandingPage.js
Normal file
77
client/src/pages/LandingPage/LandingPage.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import logo from "../../pictures/logo.png";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Button from "@material-ui/core/Button";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
link: {
|
||||
margin: theme.spacing(2, 2, 1),
|
||||
},
|
||||
}));
|
||||
|
||||
const Landing = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<img src={logo} alt="logo" style={{ width: "25%" }} />
|
||||
|
||||
<Typography variant="h5" style={{ paddingBottom: "20px", textAlign: "center" }}>
|
||||
Welcome to the HealthGeeks Patient-Management-System
|
||||
</Typography>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
className={classes.link}
|
||||
to="/auth/staff/login"
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<Button fullWidth variant="contained" color="primary">
|
||||
Staff Member
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
className={classes.link}
|
||||
to="/auth/medical/login"
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<Button fullWidth variant="contained" color="primary">
|
||||
Medical Staff
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
className={classes.link}
|
||||
to="/auth/nurse/login"
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<Button fullWidth variant="contained" color="primary">
|
||||
Charge Nurse
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Landing;
|
1
client/src/pages/LandingPage/index.js
Normal file
1
client/src/pages/LandingPage/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './LandingPage'
|
59
client/src/pages/PageNotFound/PageNotFound.js
Normal file
59
client/src/pages/PageNotFound/PageNotFound.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
import Button from '@material-ui/core/Button'
|
||||
import Home from '@material-ui/icons/Home'
|
||||
import Page from 'material-ui-shell/lib/containers/Page'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import React from 'react'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
icon: {
|
||||
width: 192,
|
||||
height: 192,
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: `100%`,
|
||||
},
|
||||
paper: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
margin: 0,
|
||||
height: `calc(100vh - 64px)`,
|
||||
},
|
||||
button: {
|
||||
marginTop: 20,
|
||||
},
|
||||
}))
|
||||
|
||||
const PageNotFound = () => {
|
||||
const intl = useIntl()
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Page pageTitle={intl.formatMessage({ id: 'page_not_found' })}>
|
||||
<Paper className={classes.paper}>
|
||||
<div className={classes.container}>
|
||||
<Typography variant="h4">404</Typography>
|
||||
<Typography variant="subtitle1">
|
||||
{intl.formatMessage({ id: 'page_not_found' })}
|
||||
</Typography>
|
||||
<Button
|
||||
color="secondary"
|
||||
aria-label="home"
|
||||
href="/"
|
||||
className={classes.button}
|
||||
>
|
||||
<Home />
|
||||
</Button>
|
||||
</div>
|
||||
</Paper>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageNotFound
|
115
client/src/pages/PasswordReset/PasswordReset.js
Normal file
115
client/src/pages/PasswordReset/PasswordReset.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
import Button from '@material-ui/core/Button'
|
||||
import Page from 'material-ui-shell/lib/containers/Page'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import React, { useState } from 'react'
|
||||
import TextField from '@material-ui/core/TextField'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useIntl } from 'react-intl'
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
width: 'auto',
|
||||
marginLeft: theme.spacing(3),
|
||||
marginRight: theme.spacing(3),
|
||||
[theme.breakpoints.up(620 + theme.spacing(6))]: {
|
||||
width: 400,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
},
|
||||
marginTop: theme.spacing(8),
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(
|
||||
3
|
||||
)}px`,
|
||||
},
|
||||
avatar: {
|
||||
margin: theme.spacing(1),
|
||||
width: 192,
|
||||
height: 192,
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
form: {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
submit: {
|
||||
margin: theme.spacing(3, 0, 2),
|
||||
},
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: `100%`,
|
||||
},
|
||||
}))
|
||||
|
||||
const SignUp = () => {
|
||||
const classes = useStyles()
|
||||
const intl = useIntl()
|
||||
const history = useHistory()
|
||||
const [username, setUsername] = useState('')
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
history.replace('/signin')
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
pageTitle={intl.formatMessage({
|
||||
id: 'password_reset',
|
||||
defaultMessage: 'Password reset',
|
||||
})}
|
||||
onBackClick={() => {
|
||||
history.goBack()
|
||||
}}
|
||||
>
|
||||
<Paper className={classes.paper} elevation={6}>
|
||||
<div className={classes.container}>
|
||||
<Typography component="h1" variant="h5">
|
||||
{intl.formatMessage({
|
||||
id: 'password_reset',
|
||||
defaultMessage: 'Password reset',
|
||||
})}
|
||||
</Typography>
|
||||
<form className={classes.form} onSubmit={handleSubmit} noValidate>
|
||||
<TextField
|
||||
value={username}
|
||||
onInput={(e) => setUsername(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="email"
|
||||
label={intl.formatMessage({
|
||||
id: 'email',
|
||||
defaultMessage: 'E-Mail',
|
||||
})}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: 'password_reset',
|
||||
defaultMessage: 'Reset Password',
|
||||
})}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</Paper>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignUp
|
5
client/src/pages/PatientContext/Buttons.js
Normal file
5
client/src/pages/PatientContext/Buttons.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import RegisterPatientButton from './RegisterPatientButton';
|
||||
import ConsultFileButton from './ConsultFileButton';
|
||||
import RequestToAdmitButton from './RequestToAdmitButton'
|
||||
|
||||
export { RegisterPatientButton, ConsultFileButton, RequestToAdmitButton };
|
28
client/src/pages/PatientContext/ConsultFileButton.js
Normal file
28
client/src/pages/PatientContext/ConsultFileButton.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useQuestions } from 'material-ui-shell/lib/providers/Dialogs/Question';
|
||||
import ConsultFileForm from './ConsultFileForm'
|
||||
|
||||
//we should have medication somewhere inside here
|
||||
function ConsultFileButton(props){
|
||||
const { openDialog, setProcessing } = useQuestions();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="default"
|
||||
onClick={() => {
|
||||
|
||||
openDialog({
|
||||
title: 'Consult File',
|
||||
message: <ConsultFileForm firstname= {props.firstname} lastname= {props.lastname} />,
|
||||
action: null,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Consult File
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConsultFileButton;
|
295
client/src/pages/PatientContext/ConsultFileForm.js
Normal file
295
client/src/pages/PatientContext/ConsultFileForm.js
Normal file
|
@ -0,0 +1,295 @@
|
|||
import React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
'& .MuiTextField-root': {
|
||||
margin: theme.spacing(2),
|
||||
width: '25ch',
|
||||
},
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(1),
|
||||
width: '25ch',
|
||||
},
|
||||
}));
|
||||
|
||||
function ConsultFileForm (props){
|
||||
|
||||
const classes = useStyles();
|
||||
const [gender, setGender] = React.useState('');
|
||||
const [maritalStatus, setMaritalStatus] = React.useState('');
|
||||
const [province, setProvince] = React.useState('');
|
||||
const [state, setState] = React.useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
telephone: '',
|
||||
addressLineOne: '',
|
||||
city: '',
|
||||
provice: '',
|
||||
postalCode: '',
|
||||
dateOfBirth: '',
|
||||
gender: '',
|
||||
maritalStatus: '',
|
||||
externalDoctor: '',
|
||||
divisionID: '',
|
||||
//admitted
|
||||
//requested
|
||||
});
|
||||
|
||||
React.useEffect( () => {
|
||||
getData(); //we get data right after render
|
||||
})
|
||||
|
||||
//GEt from backend
|
||||
function getData(){
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
//What happens after calling express goes here
|
||||
|
||||
setState( {
|
||||
firstname: data.firstname,
|
||||
lastname: data.lastname,
|
||||
telephone: data.telephone,
|
||||
addressLineOne: data.addressLineOne,
|
||||
city: data.city,
|
||||
provice: data.province,
|
||||
postalCode: data.postalCode,
|
||||
dateOfBirth: data.dateOfBirth,
|
||||
gender: data.gender,
|
||||
maritalStatus: data.maritalStatus,
|
||||
externalDoctor: data.externalDoctor,
|
||||
divisionID: data.divisionID,
|
||||
})
|
||||
}).catch(e => {
|
||||
//console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setState({
|
||||
...state,
|
||||
[e.target.id]: value
|
||||
});
|
||||
}
|
||||
const handleGender = (e) => {
|
||||
setGender(e.target.value)
|
||||
}
|
||||
const handleMaritalStatus = (e) => {
|
||||
setMaritalStatus(e.target.value)
|
||||
}
|
||||
const handleProvinceChange = (e) => {
|
||||
setProvince(e.target.value)
|
||||
}
|
||||
|
||||
const [editable, setEditable] = React.useState(false);
|
||||
const [buttonValue, setButtonValue] = React.useState('Edit');
|
||||
|
||||
function saveOrEdit(e){
|
||||
if ( editable){
|
||||
setButtonValue( 'Edit');
|
||||
setEditable(false);
|
||||
}else{
|
||||
saveData(e);
|
||||
setButtonValue('Save');
|
||||
setEditable(true);
|
||||
}
|
||||
}
|
||||
|
||||
//Send Data to Backend
|
||||
function saveData(e){
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
//What happens after calling express goes here
|
||||
}).catch(e => {
|
||||
//console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<form className={classes.root} autoComplete="off">
|
||||
<div>
|
||||
<TextField
|
||||
disabled = {!editable}
|
||||
required
|
||||
label="First Name"
|
||||
value={state.firstname}
|
||||
id="firstname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
required
|
||||
label="Last Name"
|
||||
value={state.lastname}
|
||||
id="lastname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
required
|
||||
label="Telephone Number"
|
||||
value={state.telephone}
|
||||
id="telephone"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
disabled={!editable}
|
||||
type="date"
|
||||
label="Date of Birth"
|
||||
value={state.dateOfBirth}
|
||||
id="dateOfBirth"
|
||||
onChange={handleChange}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
required
|
||||
label="Address Line One"
|
||||
value={state.addressLineOne}
|
||||
id="addressLineOne"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
required
|
||||
label="City"
|
||||
value={state.city}
|
||||
id="city"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
select
|
||||
id="userProvince"
|
||||
value={province}
|
||||
onChange={handleProvinceChange}
|
||||
label="Province"
|
||||
>
|
||||
<MenuItem value={'alberta'}>Alberta </MenuItem>
|
||||
<MenuItem value={'britishcolumbia'}>British Columbia</MenuItem>
|
||||
<MenuItem value={'newbrunswick'}>New Brunswick</MenuItem>
|
||||
<MenuItem value={'newfoundlandandlabrador'}>Newfoundland and Labrador</MenuItem>
|
||||
<MenuItem value={'novascotia'}>Nova Scotia</MenuItem>
|
||||
<MenuItem value={'princeedwardisland'}>Prince Edward Island</MenuItem>
|
||||
<MenuItem value={'ontario'}>Ontario</MenuItem>
|
||||
<MenuItem value={'quebec'}>Quebec</MenuItem>
|
||||
<MenuItem value={'saskatchewan'}>Saskatchewan</MenuItem>
|
||||
</TextField>
|
||||
|
||||
|
||||
<TextField
|
||||
required
|
||||
disabled={!editable}
|
||||
label="Postal Code"
|
||||
value={state.postalCode}
|
||||
id="postalCode"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
select
|
||||
disabled={!editable}
|
||||
required
|
||||
label="Gender"
|
||||
value={gender}
|
||||
onChange={handleGender}
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
<MenuItem value={'male'}> Male </MenuItem>
|
||||
<MenuItem value={'female'}> Female </MenuItem>
|
||||
<MenuItem value={'other'}> Other</MenuItem>
|
||||
<MenuItem value={'none'}>Prefer Not to Identify</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
select
|
||||
disabled={!editable}
|
||||
required
|
||||
label="Marital Status"
|
||||
value={maritalStatus}
|
||||
onChange={handleMaritalStatus}
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
|
||||
<MenuItem value={'married'}> Married </MenuItem>
|
||||
<MenuItem value={'single'}> Single </MenuItem>
|
||||
<MenuItem value={'commonlaw'}> Common Law</MenuItem>
|
||||
</TextField>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
required
|
||||
label="External Doctor"
|
||||
value={state.externalDoctor}
|
||||
id="telephone"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
disabled={!editable}
|
||||
label="Division ID"
|
||||
value={state.divisionID}
|
||||
id="divisionid"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<Button style={{ float: "right", marginRight: "2rem" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => saveOrEdit(e)}>
|
||||
{buttonValue}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConsultFileForm;
|
||||
|
114
client/src/pages/PatientContext/PatientList.js
Normal file
114
client/src/pages/PatientContext/PatientList.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
import React from 'react';
|
||||
import Page from 'material-ui-shell/lib/containers/Page';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TableContainer from '@material-ui/core/TableContainer';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { RegisterPatientButton, ConsultFileButton, RequestToAdmitButton } from './Buttons';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
});
|
||||
|
||||
function createData(id, firstname, lastname, division, admitted) {
|
||||
return { id, firstname, lastname, division, admitted };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData(2421, 'James', 'Iger', 6, false),
|
||||
createData( 1343, 'Bob', 'Andrews', 1, true),
|
||||
createData(5413, 'John', 'Dunderson', 3, true),
|
||||
createData(8193, 'Sam', 'Ellis', 5, false),
|
||||
|
||||
];
|
||||
|
||||
function PatientList() {
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
React.useEffect( () => {
|
||||
getData();
|
||||
})
|
||||
|
||||
function getData (){
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
};
|
||||
|
||||
fetch('/', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
//we feed the data into the function to create patient object
|
||||
const dataRows = data.map ( (patient) => {
|
||||
return createData( patient.id, patient.firstname, patient.lastname, patient.division, patient.admitted);
|
||||
})
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Page pageTitle='Summary' >
|
||||
<div style={{padding: '32px'}}>
|
||||
|
||||
<Typography variant="h3" style={{marginBottom: '16px'}}> Patient List </Typography>
|
||||
|
||||
<RegisterPatientButton />
|
||||
|
||||
<TableContainer component={Paper}>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>ID </TableCell>
|
||||
<TableCell>First Name</TableCell>
|
||||
<TableCell>Last Name</TableCell>
|
||||
<TableCell>Division</TableCell>
|
||||
<TableCell></TableCell>
|
||||
<TableCell></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell>{row.id}</TableCell>
|
||||
<TableCell>{row.firstname}</TableCell>
|
||||
<TableCell>{row.lastname}</TableCell>
|
||||
<TableCell>{row.division}</TableCell>
|
||||
<TableCell>
|
||||
<ConsultFileButton
|
||||
firstname = {row.firstname}
|
||||
lastname = {row.lastname}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell >
|
||||
<div hidden={row.admitted}>
|
||||
<RequestToAdmitButton
|
||||
firstname={row.firstname}
|
||||
lastname={row.lastname}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
</div>
|
||||
</Page>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default PatientList;
|
29
client/src/pages/PatientContext/RegisterPatientButton.js
Normal file
29
client/src/pages/PatientContext/RegisterPatientButton.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useQuestions } from 'material-ui-shell/lib/providers/Dialogs/Question';
|
||||
import RegistrationForm from './RegistrationForm';
|
||||
|
||||
function RegisterPatientButton (props) {
|
||||
|
||||
const { openDialog, setProcessing } = useQuestions();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
style={{marginBottom: '16px'}}
|
||||
onClick={() => {
|
||||
|
||||
openDialog({
|
||||
title: 'Register Patient',
|
||||
message: <RegistrationForm/>,
|
||||
action: null,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Register Patient
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default RegisterPatientButton;
|
229
client/src/pages/PatientContext/RegistrationForm.js
Normal file
229
client/src/pages/PatientContext/RegistrationForm.js
Normal file
|
@ -0,0 +1,229 @@
|
|||
import React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
'& .MuiTextField-root': {
|
||||
margin: theme.spacing(2),
|
||||
width: '25ch',
|
||||
},
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(1),
|
||||
width: '25ch',
|
||||
},
|
||||
}));
|
||||
|
||||
function RegistrationForm() {
|
||||
const classes = useStyles();
|
||||
const [gender, setGender] = React.useState('');
|
||||
const [maritalStatus, setMaritalStatus] = React.useState('');
|
||||
const [province, setProvince] = React.useState('alberta');
|
||||
const [state, setState] = React.useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
telephone: '',
|
||||
addressLineOne: '',
|
||||
city: '',
|
||||
provice: '',
|
||||
postalCode: '',
|
||||
dateOfBirth: '',
|
||||
gender : '',
|
||||
maritalStatus: '',
|
||||
externalDoctor: '',
|
||||
divisionID: '',
|
||||
//admitted
|
||||
//requested
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setState( {
|
||||
...state,
|
||||
[e.target.id] : value
|
||||
});
|
||||
}
|
||||
|
||||
const handleGender = (e) => {
|
||||
setGender(e.target.value)
|
||||
}
|
||||
|
||||
const handleMaritalStatus = (e) => {
|
||||
setMaritalStatus(e.target.value)
|
||||
}
|
||||
|
||||
const handleProvinceChange = (e) => {
|
||||
setProvince(e.target.value)
|
||||
}
|
||||
|
||||
//We call the API HERE
|
||||
const register = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
//What happens after calling express goes here
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form className={classes.root} autoComplete="off">
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="First Name"
|
||||
value = {state.firstname}
|
||||
id = "firstname"
|
||||
onChange = {handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Last Name"
|
||||
value={state.lastname}
|
||||
id="lastname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Telephone Number"
|
||||
value={state.telephone}
|
||||
id="telephone"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="date"
|
||||
label="Date of Birth"
|
||||
value={state.dateOfBirth}
|
||||
id="dateOfBirth"
|
||||
onChange={handleChange}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Address Line One"
|
||||
value={state.addressLineOne}
|
||||
id="addressLineOne"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="City"
|
||||
value={state.city}
|
||||
id="city"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
select
|
||||
id="userProvince"
|
||||
value={province}
|
||||
onChange={handleProvinceChange}
|
||||
label="Province"
|
||||
>
|
||||
<MenuItem value={'alberta'}>Alberta </MenuItem>
|
||||
<MenuItem value={'britishcolumbia'}>British Columbia</MenuItem>
|
||||
<MenuItem value={'newbrunswick'}>New Brunswick</MenuItem>
|
||||
<MenuItem value={'newfoundlandandlabrador'}>Newfoundland and Labrador</MenuItem>
|
||||
<MenuItem value={'novascotia'}>Nova Scotia</MenuItem>
|
||||
<MenuItem value={'princeedwardisland'}>Prince Edward Island</MenuItem>
|
||||
<MenuItem value={'ontario'}>Ontario</MenuItem>
|
||||
<MenuItem value={'quebec'}>Quebec</MenuItem>
|
||||
<MenuItem value={'saskatchewan'}>Saskatchewan</MenuItem>
|
||||
</TextField>
|
||||
|
||||
|
||||
<TextField
|
||||
required
|
||||
label="Postal Code"
|
||||
value={state.postalCode}
|
||||
id="postalCode"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
select
|
||||
required
|
||||
label="Gender"
|
||||
value={ gender}
|
||||
onChange={ handleGender }
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
<MenuItem value={'male'}> Male </MenuItem>
|
||||
<MenuItem value={'female'}> Female </MenuItem>
|
||||
<MenuItem value={'other'}> Other</MenuItem>
|
||||
<MenuItem value={'none'}>Prefer Not to Identify</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<TextField
|
||||
select
|
||||
required
|
||||
label="Marital Status"
|
||||
value={maritalStatus}
|
||||
onChange={handleMaritalStatus}
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
|
||||
<MenuItem value={'married'}> Married </MenuItem>
|
||||
<MenuItem value={'single'}> Single </MenuItem>
|
||||
<MenuItem value={'commonlaw'}> Common Law</MenuItem>
|
||||
</TextField>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="External Doctor"
|
||||
value={state.externalDoctor}
|
||||
id="telephone"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
label="Division ID"
|
||||
value={state.divisionID}
|
||||
id="divisionid"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
</div>
|
||||
<Button style={{ float: "right", marginRight: "3rem" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick = { (e) => register(e)}>
|
||||
Register
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RegistrationForm;
|
76
client/src/pages/PatientContext/RequestToAdmitButton.js
Normal file
76
client/src/pages/PatientContext/RequestToAdmitButton.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
import React from 'react';
|
||||
import { Button } from '@material-ui/core';
|
||||
import Snackbar from '@material-ui/core/Snackbar';
|
||||
import MuiAlert from '@material-ui/lab/Alert';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
function Alert(props) {
|
||||
return <MuiAlert elevation={6} variant="filled" {...props} />;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
'& > * + *': {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function RequestToAdmitButton() {
|
||||
|
||||
const classes = useStyles();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const handleClick = (e) => {
|
||||
//send to database
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
//create a request for admission here
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.catch(e => {
|
||||
//console.error(e);
|
||||
});
|
||||
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = (event, reason) => {
|
||||
if (reason === 'clickaway') {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="default"
|
||||
onClick={ handleClick } >
|
||||
Request To Admit
|
||||
</Button>
|
||||
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
|
||||
<Alert onClose={handleClose} severity="success">
|
||||
Successfully Requested Patient for Admission
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RequestToAdmitButton;
|
3
client/src/pages/PatientContext/index.js
Normal file
3
client/src/pages/PatientContext/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import PatientList from './PatientList';
|
||||
|
||||
export default PatientList;
|
46
client/src/pages/SignIn/SignIn.js
Normal file
46
client/src/pages/SignIn/SignIn.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { replace } from 'react-router-redux';
|
||||
|
||||
import { Action } from '../../reducers';
|
||||
import SignInComponent from './SignInComponent';
|
||||
|
||||
class Login extends React.Component {
|
||||
handleLogin(email, password) {
|
||||
this.props.login(email, password);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
if (this.props.authenticated) {
|
||||
this.props.redirectHome();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.authenticated) {
|
||||
this.props.redirectHome();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <SignInComponent onsubmit={this.handleLogin.bind(this)} error={this.props.error} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
error: state.Auth.get('error'),
|
||||
isAdmin: state.Auth.get('isAdmin'),
|
||||
timestamp: state.Auth.get('timestamp'),
|
||||
authenticated: state.Auth.get('authenticated'),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
login: (email, password) => {
|
||||
dispatch(Action.LoginUser(email, password));
|
||||
},
|
||||
redirectHome: () => {
|
||||
dispatch(replace('/home'));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Login);
|
179
client/src/pages/SignIn/SignInComponent.js
Normal file
179
client/src/pages/SignIn/SignInComponent.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Page from "material-ui-shell/lib/containers/Page";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import React, { useState } from "react";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Link } from "react-router-dom";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useAuth } from "base-shell/lib/providers/Auth";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useIntl } from "react-intl";
|
||||
import { useMenu } from "material-ui-shell/lib/providers/Menu";
|
||||
import { getDisplayUserType } from "../../utils/displayUserType";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
width: "auto",
|
||||
marginLeft: theme.spacing(3),
|
||||
marginRight: theme.spacing(3),
|
||||
[theme.breakpoints.up(620 + theme.spacing(6))]: {
|
||||
width: 400,
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
},
|
||||
marginTop: theme.spacing(8),
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(
|
||||
3
|
||||
)}px`,
|
||||
},
|
||||
avatar: {
|
||||
margin: theme.spacing(1),
|
||||
width: 192,
|
||||
height: 192,
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
form: {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
submit: {
|
||||
margin: theme.spacing(3, 0, 2),
|
||||
},
|
||||
container: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: `100%`,
|
||||
},
|
||||
}));
|
||||
|
||||
const SignInComponent = (props) => {
|
||||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
const userType = history.location.pathname.split("/")[2]; // pathname: /auth/staff/login
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [disableForm, setDisableForm] = useState(false);
|
||||
const { setAuthMenuOpen } = useMenu();
|
||||
const { setAuth } = useAuth();
|
||||
|
||||
const displayUserType = getDisplayUserType(userType);
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
// authenticate({
|
||||
// displayName: "User",
|
||||
// email: email,
|
||||
// });
|
||||
const { onsubmit } = props;
|
||||
if (!disableForm) {
|
||||
setDisableForm(true);
|
||||
onsubmit(email, password);
|
||||
setAuth({ isAuthenticated: true });
|
||||
setAuthMenuOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
const authenticate = (user) => {
|
||||
setAuth({ isAuthenticated: true, ...user });
|
||||
setAuthMenuOpen(false);
|
||||
|
||||
let _location = history.location;
|
||||
|
||||
let _route = "/home";
|
||||
if (_location.state && _location.state.from) {
|
||||
_route = _location.state.from.pathname;
|
||||
history.push(_route);
|
||||
} else {
|
||||
history.push(_route);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Page pageTitle={intl.formatMessage({ id: "sign_in" })}>
|
||||
<Paper className={classes.paper} elevation={6}>
|
||||
<div className={classes.container}>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h5"
|
||||
style={{ textTransform: "capitalize" }}
|
||||
>
|
||||
{`${displayUserType} ${intl.formatMessage({ id: "sign_in" })}`}
|
||||
</Typography>
|
||||
<form className={classes.form} onSubmit={handleSubmit} noValidate>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={email}
|
||||
onInput={(e) => setEmail(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="email"
|
||||
label={intl.formatMessage({
|
||||
id: "email",
|
||||
defaultMessage: "E-Mail",
|
||||
})}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
/>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={password}
|
||||
onInput={(e) => setPassword(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="password"
|
||||
label={intl.formatMessage({ id: "password" })}
|
||||
type="password"
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
{props.error ? (
|
||||
<span>
|
||||
<b>Error</b>: {props.error}
|
||||
</span>
|
||||
) : (
|
||||
<span> </span>
|
||||
)}
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
{intl.formatMessage({ id: "sign_in" })}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Link to="/password_reset">
|
||||
{intl.formatMessage({ id: "forgot_password" })}?
|
||||
</Link>
|
||||
<Link to={`/auth/${userType}/signup`}>
|
||||
{intl.formatMessage({ id: "registration" })}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInComponent;
|
53
client/src/pages/SignUp/SignUp.js
Normal file
53
client/src/pages/SignUp/SignUp.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SignUpComponent from './SignUpComponent';
|
||||
import { Action } from 'reducers';
|
||||
|
||||
|
||||
class SignUpContainer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.createUser = this.createUser.bind(this);
|
||||
}
|
||||
|
||||
createUser(profile) {
|
||||
this.props.registerUser(profile);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.registered) {
|
||||
setTimeout(() => {
|
||||
this.props.registerDone();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SignUpComponent
|
||||
createUser={this.createUser}
|
||||
created={this.props.registered}
|
||||
createSuccess={this.props.registerSuccess}
|
||||
createError={this.props.error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
user: state.Registration.get('user'),
|
||||
error: state.Registration.get('error'),
|
||||
registered: state.Registration.get('registered'),
|
||||
registerSuccess: state.Registration.get('registerSuccess'),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
registerUser: (newuser) => {
|
||||
dispatch(Action.RegisterUser(newuser));
|
||||
},
|
||||
registerDone: () => {
|
||||
dispatch(Action.registerDone());
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignUpContainer);
|
209
client/src/pages/SignUp/SignUpComponent.js
Normal file
209
client/src/pages/SignUp/SignUpComponent.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
import Button from "@material-ui/core/Button";
|
||||
import Page from "material-ui-shell/lib/containers/Page";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import React, { useState } from "react";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { useAuth } from "base-shell/lib/providers/Auth";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useIntl } from "react-intl";
|
||||
import { useMenu } from "material-ui-shell/lib/providers/Menu";
|
||||
import { getDisplayUserType } from "../../utils/displayUserType";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
width: "auto",
|
||||
marginLeft: theme.spacing(3),
|
||||
marginRight: theme.spacing(3),
|
||||
[theme.breakpoints.up(620 + theme.spacing(6))]: {
|
||||
width: 400,
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
},
|
||||
marginTop: theme.spacing(8),
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
padding: `${theme.spacing(2)}px ${theme.spacing(3)}px ${theme.spacing(
|
||||
3
|
||||
)}px`,
|
||||
},
|
||||
avatar: {
|
||||
margin: theme.spacing(1),
|
||||
width: 192,
|
||||
height: 192,
|
||||
color: theme.palette.secondary.main,
|
||||
},
|
||||
form: {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
submit: {
|
||||
margin: theme.spacing(3, 0, 2),
|
||||
},
|
||||
container: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: `100%`,
|
||||
},
|
||||
}));
|
||||
|
||||
const SignUp = (props) => {
|
||||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [userEmail, setUserEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [disableForm, setDisableForm] = useState(false);
|
||||
|
||||
const userType = history.location.pathname.split("/")[2]; // pathname ex: /auth/staff/login
|
||||
const { setAuthMenuOpen } = useMenu();
|
||||
const { setAuth } = useAuth();
|
||||
|
||||
const displayUserType = getDisplayUserType(userType);
|
||||
|
||||
function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
if (!disableForm) {
|
||||
if (
|
||||
firstName &&
|
||||
lastName &&
|
||||
userEmail &&
|
||||
password &&
|
||||
password.length >= 4
|
||||
) {
|
||||
setDisableForm(true);
|
||||
props.createUser({
|
||||
firstName,
|
||||
lastName,
|
||||
email:userEmail,
|
||||
password,
|
||||
confirmPassword,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setAuth({ isAuthenticated: true });
|
||||
|
||||
setAuthMenuOpen(false);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
pageTitle={intl.formatMessage({
|
||||
id: "sign_up",
|
||||
defaultMessage: " Sign up",
|
||||
})}
|
||||
onBackClick={() => {
|
||||
history.goBack();
|
||||
}}
|
||||
>
|
||||
<Paper className={classes.paper} elevation={6}>
|
||||
<div className={classes.container}>
|
||||
<Typography component="h1" variant="h5">
|
||||
{`${displayUserType} ${intl.formatMessage({
|
||||
id: "sign_up",
|
||||
defaultMessage: "Sign up",
|
||||
})}`}
|
||||
</Typography>
|
||||
<form className={classes.form} onSubmit={handleSubmit}>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={firstName}
|
||||
onInput={(e) => setFirstName(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="first-name"
|
||||
label="First Name"
|
||||
name="first-name"
|
||||
autoComplete="first-name"
|
||||
/>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={lastName}
|
||||
onInput={(e) => setLastName(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="last-name"
|
||||
label="Last Name"
|
||||
name="last-name"
|
||||
autoComplete="last-name"
|
||||
/>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={userEmail}
|
||||
onInput={(e) => setUserEmail(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="email"
|
||||
label={intl.formatMessage({
|
||||
id: "email",
|
||||
defaultMessage: "E-Mail",
|
||||
})}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
/>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={password}
|
||||
onInput={(e) => setPassword(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="password"
|
||||
label={intl.formatMessage({
|
||||
id: "password",
|
||||
defaultMessage: "Password",
|
||||
})}
|
||||
type="password"
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<TextField
|
||||
disabled={disableForm}
|
||||
value={confirmPassword}
|
||||
onInput={(e) => setConfirmPassword(e.target.value)}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="password_confirm"
|
||||
label={intl.formatMessage({
|
||||
id: "password_confirm",
|
||||
defaultMessage: "Confirm Password",
|
||||
})}
|
||||
type="password"
|
||||
id="password_confirm"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
{intl.formatMessage({ id: "sign_up", defaultMessage: "Sign up" })}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</Paper>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignUp;
|
100
client/src/pages/StaffContext/AdmissionRequests.js
Normal file
100
client/src/pages/StaffContext/AdmissionRequests.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
import React from 'react';
|
||||
import Page from 'material-ui-shell/lib/containers/Page';
|
||||
import Table from '@material-ui/core/Table';
|
||||
import TableBody from '@material-ui/core/TableBody';
|
||||
import TableCell from '@material-ui/core/TableCell';
|
||||
import TableContainer from '@material-ui/core/TableContainer';
|
||||
import TableHead from '@material-ui/core/TableHead';
|
||||
import TableRow from '@material-ui/core/TableRow';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import AdmitButton from './AdmitButton';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
minWidth: 650,
|
||||
},
|
||||
});
|
||||
|
||||
function createData(id, firstname, lastname) {
|
||||
return { id, firstname, lastname };
|
||||
}
|
||||
|
||||
const rows = [
|
||||
createData(2421, 'James', 'Iger' ),
|
||||
createData(1343, 'Bob', 'Andrews'),
|
||||
createData(5413, 'John', 'Dunderson'),
|
||||
createData(8193, 'Sam', 'Ellis' ),
|
||||
];
|
||||
|
||||
function AdmissionRequests() {
|
||||
|
||||
const classes = useStyles();
|
||||
React.useEffect( () => {
|
||||
getData();
|
||||
})
|
||||
|
||||
function getData(){
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
};
|
||||
|
||||
fetch('/', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
//we feed the data into the function to create patient object
|
||||
const dataRows = data.map((patient) => {
|
||||
return createData(patient.id, patient.firstname, patient.lastname );
|
||||
})
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Page pageTitle='Summary' >
|
||||
|
||||
<Typography variant="h4" style={{marginBottom: 16}}> Admission Requests </Typography>
|
||||
|
||||
|
||||
<TableContainer component={Paper}>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>ID </TableCell>
|
||||
<TableCell>First Name</TableCell>
|
||||
<TableCell>Last Name</TableCell>
|
||||
<TableCell> Action </TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell>{row.id}</TableCell>
|
||||
<TableCell>{row.firstname}</TableCell>
|
||||
<TableCell>{row.lastname}</TableCell>
|
||||
<TableCell >
|
||||
<AdmitButton
|
||||
firstname = {row.firstname}
|
||||
lastname = {row.lastname}>
|
||||
</AdmitButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
</Page>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default AdmissionRequests;
|
27
client/src/pages/StaffContext/AdmitButton.js
Normal file
27
client/src/pages/StaffContext/AdmitButton.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import { useQuestions } from 'material-ui-shell/lib/providers/Dialogs/Question';
|
||||
import { Button } from '@material-ui/core';
|
||||
import AdmitForm from './AdmitForm';
|
||||
|
||||
function AdmitButton(props) {
|
||||
const { openDialog, setProcessing } = useQuestions();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="default"
|
||||
onClick={() => {
|
||||
|
||||
openDialog({
|
||||
title: 'Consult File',
|
||||
message: <AdmitForm firstname={props.firstname} lastname={props.lastname}/> ,
|
||||
action: null,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Admit Patient
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdmitButton;
|
137
client/src/pages/StaffContext/AdmitForm.js
Normal file
137
client/src/pages/StaffContext/AdmitForm.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
import React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
'& .MuiTextField-root': {
|
||||
margin: theme.spacing(2),
|
||||
width: '25ch',
|
||||
},
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(1),
|
||||
width: '25ch',
|
||||
},
|
||||
}));
|
||||
|
||||
function AdmitForm(props) {
|
||||
const classes = useStyles();
|
||||
const [state, setState] = React.useState({
|
||||
firstname: props.firstname,
|
||||
lastname: props.lastname,
|
||||
division: '',
|
||||
localdoctor: '',
|
||||
roomnumber: '',
|
||||
bednumber: '',
|
||||
privateinsurancenumber: '',
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setState({
|
||||
...state,
|
||||
[e.target.id]: value
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//We call the API HERE
|
||||
const admit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
//What happens after calling express goes here
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form className={classes.root} autoComplete="off">
|
||||
<div>
|
||||
<TextField
|
||||
disabled
|
||||
label="First Name"
|
||||
value={state.firstname}
|
||||
/>
|
||||
<TextField
|
||||
disabled
|
||||
label="Last Name"
|
||||
value={state.lastname}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Division"
|
||||
value={state.division}
|
||||
id="division"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Local Doctor"
|
||||
value={state.localdoctor}
|
||||
id="localdoctor"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Room Number"
|
||||
value={state.roomnumber}
|
||||
id="roomnumber"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Bed Number"
|
||||
value={state.bednumber}
|
||||
id="bednumber"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label="Private Insurance Number"
|
||||
value={state.privateinsurancenumber}
|
||||
id="privateinsurancenumber"
|
||||
onChange={handleChange}
|
||||
helperText= "Optional"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<Button style={{ float: "right", marginRight: "3rem" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => admit(e)}>
|
||||
Confirm Admission
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdmitForm;
|
113
client/src/pages/StaffContext/DischargeForm.js
Normal file
113
client/src/pages/StaffContext/DischargeForm.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
import React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { Button } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
'& .MuiTextField-root': {
|
||||
margin: theme.spacing(2),
|
||||
width: '25ch',
|
||||
},
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(1),
|
||||
width: '25ch',
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
function DischargeForm() {
|
||||
|
||||
const classes = useStyles();
|
||||
const [state, setState] = React.useState({
|
||||
id: '',
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
division: '',
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setState({
|
||||
...state,
|
||||
[e.target.id]: value
|
||||
});
|
||||
}
|
||||
|
||||
const discharge = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
//What happens after calling express goes here
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form className={classes.root} autoComplete="off">
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="ID"
|
||||
value={state.id}
|
||||
id="id"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
required
|
||||
label="Division"
|
||||
value={state.division}
|
||||
id="division"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="First Name"
|
||||
value={state.firstname}
|
||||
id="firstname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="First Name"
|
||||
value={state.firstname}
|
||||
id="firstname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<Button style={{ float: "right", marginRight: "3rem" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => discharge(e)}
|
||||
value = "Confirm Discharge">
|
||||
Confirm Discharge
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DischargeForm;
|
27
client/src/pages/StaffContext/DischargePatient.js
Normal file
27
client/src/pages/StaffContext/DischargePatient.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import { useQuestions } from 'material-ui-shell/lib/providers/Dialogs/Question';
|
||||
import { Button } from '@material-ui/core';
|
||||
import DischargeForm from './DischargeForm';
|
||||
|
||||
function DischargePatient(props) {
|
||||
const { openDialog, setProcessing } = useQuestions();
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
|
||||
openDialog({
|
||||
title: 'Consult File',
|
||||
message: <DischargeForm/>,
|
||||
action: null,
|
||||
})
|
||||
}}
|
||||
>
|
||||
Discharge Patient
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default DischargePatient;
|
229
client/src/pages/StaffContext/PrescribeMedications.js
Normal file
229
client/src/pages/StaffContext/PrescribeMedications.js
Normal file
|
@ -0,0 +1,229 @@
|
|||
import React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { Typography } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
'& .MuiTextField-root': {
|
||||
margin: theme.spacing(2),
|
||||
width: '25ch',
|
||||
},
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(1),
|
||||
width: '25ch',
|
||||
},
|
||||
}));
|
||||
|
||||
function PrescribeMedications(){
|
||||
const classes = useStyles();
|
||||
const [state, setState] = React.useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
drugnumber: '',
|
||||
drugname: '',
|
||||
unitsbyday: '',
|
||||
numberofadmin: '',
|
||||
administrationtime1: '',
|
||||
administrationunits1: '',
|
||||
administrationtime2: '',
|
||||
administrationunits2: '',
|
||||
administrationtime3: '',
|
||||
administrationunits3: '',
|
||||
methodofadmin: '',
|
||||
startdate: '',
|
||||
finishdate: '',
|
||||
})
|
||||
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setState({
|
||||
...state,
|
||||
[e.target.id]: value
|
||||
});
|
||||
}
|
||||
|
||||
const prescribe = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
var data = { // what to send goes here
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
mode: 'cors',
|
||||
body: JSON.stringify(data)
|
||||
};
|
||||
|
||||
fetch('/ ', options)
|
||||
.then(res => res.json())
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
//What happens after calling express goes here
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
< Typography variant="h5" style={{padding: "1rem"}}> Prescribe Medications </Typography>
|
||||
|
||||
<form className={classes.root} autoComplete="off">
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="First Name"
|
||||
value={state.firstname}
|
||||
id="firstname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Last Name"
|
||||
value={state.lastname}
|
||||
id="lastname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Drug Number"
|
||||
value={state.drugnumber}
|
||||
id="drugnumber"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
label="Drug Name"
|
||||
value={state.drugname}
|
||||
id="drugname"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Units by Day"
|
||||
value={state.unitsbyday}
|
||||
id="unitsbyday"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Typography variant="h6" style={{ padding: "1rem" }}> Admin Information</Typography>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label="Number of Administrations"
|
||||
value={state.numberofadmin}
|
||||
id="numberofadmin"
|
||||
onChange={handleChange}
|
||||
helperText="Per Day"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
type="time"
|
||||
value={state.adminstrationtime1}
|
||||
id="adminstrationtime1"
|
||||
onChange={handleChange}
|
||||
helperText="Admnistration Time 1"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Admnistration Units 1"
|
||||
value={state.administrationunits1}
|
||||
id="administrationunits1"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
type="time"
|
||||
value={state.adminstrationtime2}
|
||||
id="adminstrationtime1"
|
||||
onChange={handleChange}
|
||||
helperText="Admnistration Time 2"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Admnistration Units 1"
|
||||
value={state.administrationunits2}
|
||||
id="administrationunits2"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
type="time"
|
||||
value={state.adminstrationtime3}
|
||||
id="adminstrationtime3"
|
||||
onChange={handleChange}
|
||||
helperText="Admnistration Time 3"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Admnistration Units 3"
|
||||
value={state.administrationunits3}
|
||||
id="administrationunits3"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
required
|
||||
label ="Method of Administration"
|
||||
value={state.methodofadmin}
|
||||
id="methodofadmin"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
type="date"
|
||||
value={state.startdate}
|
||||
id="startdate"
|
||||
onChange={handleChange}
|
||||
helperText="Start Date"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
type="date"
|
||||
value={state.finishdate}
|
||||
id="finishdate"
|
||||
onChange={handleChange}
|
||||
helperText="Finish Date"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<Button style={{ float: "right", marginRight: "3rem" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => prescribe(e)}>
|
||||
Prescibe Medication
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PrescribeMedications;
|
36
client/src/pages/StaffContext/StaffContext.js
Normal file
36
client/src/pages/StaffContext/StaffContext.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React from 'react';
|
||||
import Page from 'material-ui-shell/lib/containers/Page';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import AdmissionRequests from './AdmissionRequests';
|
||||
import PrescribeMedications from './PrescribeMedications';
|
||||
import DischargePatient from './DischargePatient';
|
||||
|
||||
const StaffContext = (props) => {
|
||||
|
||||
//interchange between "CHARGE NURSE" and "MEDICAL" to see different actions
|
||||
//get this using (props.accessType) or similar. Send props in wrapper
|
||||
const [accessType, setAccessType] = React.useState('CHARGE NURSE') ;
|
||||
|
||||
if ( accessType === 'MEDICAL'){
|
||||
return (
|
||||
<Page pageTitle='Staff Context' >
|
||||
<Typography variant="h4" style={{ padding: "1rem" }}> Available Actions for Doctor</Typography>
|
||||
<PrescribeMedications/>
|
||||
|
||||
</Page>
|
||||
)
|
||||
} else if (accessType === "CHARGE NURSE"){
|
||||
return (
|
||||
<Page pageTitle='Staff Context' >
|
||||
<div style={{padding: 64}}>
|
||||
|
||||
<Typography variant="h4" style={{marginBottom: 16}}> Available Actions for Charge Nurse</Typography>
|
||||
<DischargePatient />
|
||||
<AdmissionRequests/>
|
||||
</div>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
export default StaffContext;
|
3
client/src/pages/StaffContext/index.js
Normal file
3
client/src/pages/StaffContext/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import StaffContext from "./StaffContext";
|
||||
|
||||
export default StaffContext;
|
72
client/src/pages/StaffContext/wrapper.js
Normal file
72
client/src/pages/StaffContext/wrapper.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Action } from 'reducers';
|
||||
import StaffContext from './StaffContext';
|
||||
|
||||
|
||||
class Home extends React.Component {
|
||||
|
||||
componentWillMount() {
|
||||
if (this.props.isAdmin) {
|
||||
return this.props.redirectHome();
|
||||
}
|
||||
if (this.props.authenticated) {
|
||||
this.props.fetchUser();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isAdmin) {
|
||||
return nextProps.redirectHome();
|
||||
}
|
||||
if (nextProps.updated) {
|
||||
setTimeout(() => {
|
||||
this.props.updateDone();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StaffContext accessType = { this.props.profile.role}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const profile = {
|
||||
name: '',
|
||||
role: '',
|
||||
};
|
||||
|
||||
if (state.User.get('fetchSuccess')) {
|
||||
const User = state.User.get('profile');
|
||||
profile.name = `${User.firstName} ${User.lastName}`;
|
||||
profile.role = User.accessType;
|
||||
}
|
||||
|
||||
return {
|
||||
profile,
|
||||
fetchSuccess: state.User.get('fetchSuccess'),
|
||||
updated: state.User.get('updated'),
|
||||
updateSuccess: state.User.get('updateSuccess'),
|
||||
updateError: state.User.get('error'),
|
||||
authenticated: state.Auth.get('authenticated'),
|
||||
isAdmin: state.Auth.get('isAdmin'),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchUser: () => {
|
||||
dispatch(Action.FetchUser());
|
||||
},
|
||||
updateUser: (newprofile) => {
|
||||
dispatch(Action.UpdateUser(newprofile));
|
||||
},
|
||||
updateDone: () => {
|
||||
dispatch(Action.UserUpdateDone());
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Home);
|
BIN
client/src/pictures/logo.png
Normal file
BIN
client/src/pictures/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
131
client/src/reducers/auth.js
Normal file
131
client/src/reducers/auth.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
import config from '../config/config'
|
||||
import storage from '../lib/storage';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
import { replace } from 'react-router-redux';
|
||||
|
||||
/** ********************************************
|
||||
** Constants **
|
||||
******************************************** */
|
||||
|
||||
const AUTH_USER = Symbol();
|
||||
const UNAUTH_USER = Symbol();
|
||||
const AUTH_ERROR = Symbol();
|
||||
|
||||
const initState = () => {
|
||||
const token = storage.get('token');
|
||||
return Immutable.fromJS({
|
||||
error: null,
|
||||
timestamp: null,
|
||||
authenticated: !!token,
|
||||
isAdmin: !!token && tokenIsAdmin(token),
|
||||
});
|
||||
};
|
||||
|
||||
/** ********************************************
|
||||
** Helper Functions **
|
||||
******************************************** */
|
||||
|
||||
const tokenGetClaims = (token) => {
|
||||
if (!token) {
|
||||
return {};
|
||||
}
|
||||
const tokenArray = token.split('.');
|
||||
if (tokenArray.length !== 3) {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(window.atob(tokenArray[1].replace('-', '+').replace('_', '/')));
|
||||
};
|
||||
|
||||
const tokenIsAdmin = token => !!tokenGetClaims(token).admin;
|
||||
|
||||
/** ********************************************
|
||||
** Auth States **
|
||||
******************************************** */
|
||||
|
||||
class State {
|
||||
static Auth(error, token) {
|
||||
return {
|
||||
type: error ? AUTH_ERROR : AUTH_USER,
|
||||
isAdmin: error ? undefined : tokenIsAdmin(token),
|
||||
error: error || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static UnAuth(error) {
|
||||
return {
|
||||
type: UNAUTH_USER,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** ********************************************
|
||||
** Actions **
|
||||
******************************************** */
|
||||
|
||||
const LoginUser = (email, password) => async (dispatch) => {
|
||||
try {
|
||||
const response = await fetch(config.apiRoutes.API_URL + config.apiRoutes.auth.login, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
|
||||
const status = await response.status;
|
||||
const data = await response.json();
|
||||
|
||||
if (!data) throw new Error('Empty response from server');
|
||||
if (data.error) throw new Error(data.error.message);
|
||||
|
||||
storage.set('token', data.token);
|
||||
dispatch(State.Auth(null, data.token));
|
||||
} catch (err) {
|
||||
dispatch(State.Auth(err.message));
|
||||
}
|
||||
};
|
||||
|
||||
const LogoutUser = error => async (dispatch) => {
|
||||
console.log('here')
|
||||
dispatch(State.UnAuth());
|
||||
storage.remove('token');
|
||||
storage.remove('auth');
|
||||
dispatch(replace('/login'));
|
||||
};
|
||||
|
||||
/** ********************************************
|
||||
** Auth Reducer **
|
||||
******************************************** */
|
||||
|
||||
const Auth = (state = initState(), action) => {
|
||||
switch (action.type) {
|
||||
case AUTH_USER:
|
||||
return state.withMutations((val) => {
|
||||
val.set('error', null);
|
||||
val.set('timestamp', Date.now());
|
||||
val.set('authenticated', true);
|
||||
val.set('isAdmin', action.isAdmin);
|
||||
});
|
||||
|
||||
case UNAUTH_USER:
|
||||
return state.withMutations((val) => {
|
||||
val.set('authenticated', false);
|
||||
val.set('isAdmin', false);
|
||||
});
|
||||
|
||||
case AUTH_ERROR:
|
||||
return state.withMutations((val) => {
|
||||
val.set('error', action.error);
|
||||
val.set('timestamp', Date.now());
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
Auth, LoginUser, LogoutUser
|
||||
};
|
37
client/src/reducers/index.js
Normal file
37
client/src/reducers/index.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
|
||||
import { routerReducer } from "react-router-redux";
|
||||
import thunk from "redux-thunk";
|
||||
|
||||
import { Auth, LoginUser, LogoutUser } from "./auth";
|
||||
import { Registration, RegisterUser, registerDone } from "./registration";
|
||||
import {
|
||||
User,
|
||||
FetchUser,
|
||||
UpdateUser,
|
||||
UserUpdateDone,
|
||||
} from "./user";
|
||||
|
||||
const store = createStore(
|
||||
combineReducers({
|
||||
Auth,
|
||||
Registration,
|
||||
User,
|
||||
router: routerReducer,
|
||||
}),
|
||||
compose(
|
||||
applyMiddleware(thunk),
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
)
|
||||
);
|
||||
|
||||
const Action = {
|
||||
LoginUser,
|
||||
LogoutUser,
|
||||
RegisterUser,
|
||||
registerDone,
|
||||
FetchUser,
|
||||
UpdateUser,
|
||||
UserUpdateDone,
|
||||
};
|
||||
|
||||
export { store, Action };
|
96
client/src/reducers/registration.js
Normal file
96
client/src/reducers/registration.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
import config from '../config/config';
|
||||
import Immutable from 'immutable';
|
||||
import { Action } from 'reducers';
|
||||
|
||||
/** ********************************************
|
||||
** Constants **
|
||||
******************************************** */
|
||||
|
||||
const REGISTER_SUCCESS = Symbol();
|
||||
const REGISTER_ERR = Symbol();
|
||||
const REGISTER_DONE = Symbol();
|
||||
|
||||
const defaultState = Immutable.fromJS({
|
||||
user: {},
|
||||
registered: false,
|
||||
registerSuccess: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
/** ********************************************
|
||||
** Registration States **
|
||||
******************************************** */
|
||||
|
||||
class State {
|
||||
static Register(error, user) {
|
||||
return {
|
||||
type: error ? REGISTER_ERR : REGISTER_SUCCESS,
|
||||
user: error ? undefined : user,
|
||||
error: error || undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** ********************************************
|
||||
** Actions **
|
||||
******************************************** */
|
||||
|
||||
const RegisterUser = user => async (dispatch) => {
|
||||
try {
|
||||
const response = await fetch(config.apiRoutes.API_URL + config.apiRoutes.auth.register, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user }),
|
||||
});
|
||||
|
||||
const status = await response.status;
|
||||
const data = await response.json();
|
||||
|
||||
if (!data) throw new Error('Empty response from server');
|
||||
if (data.error) throw new Error(data.error.message);
|
||||
|
||||
dispatch(State.Register(null, data.user));
|
||||
dispatch(Action.LoginUser(user.email, user.password));
|
||||
} catch (err) {
|
||||
dispatch(State.Register(err.message));
|
||||
}
|
||||
};
|
||||
|
||||
/** ********************************************
|
||||
** Registration Reducer **
|
||||
******************************************** */
|
||||
|
||||
const Registration = (state = defaultState, action) => {
|
||||
switch (action.type) {
|
||||
case REGISTER_SUCCESS:
|
||||
return state.withMutations((val) => {
|
||||
val.set('user', action.user);
|
||||
val.set('error', null);
|
||||
val.set('registered', true);
|
||||
val.set('registerSuccess', true);
|
||||
});
|
||||
|
||||
case REGISTER_ERR:
|
||||
return state.withMutations((val) => {
|
||||
val.set('error', action.error);
|
||||
val.set('registered', true);
|
||||
val.set('registerSuccess', false);
|
||||
});
|
||||
|
||||
case REGISTER_DONE:
|
||||
return state.withMutations((val) => {
|
||||
val.set('registered', false);
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const registerDone = () => ({ type: REGISTER_DONE });
|
||||
export {
|
||||
Registration, RegisterUser, registerDone,
|
||||
};
|
150
client/src/reducers/user.js
Normal file
150
client/src/reducers/user.js
Normal file
|
@ -0,0 +1,150 @@
|
|||
import Immutable from "immutable";
|
||||
import storage from '../lib/storage';
|
||||
import config from '../config/config';
|
||||
|
||||
import { LogoutUser } from "./auth";
|
||||
|
||||
/** ********************************************
|
||||
** Constants **
|
||||
******************************************** */
|
||||
|
||||
const FETCH_USER = Symbol();
|
||||
const FETCH_USER_ERR = Symbol();
|
||||
|
||||
const UPDATE_USER_ERR = Symbol();
|
||||
const UPDATE_USER_SUCCESS = Symbol();
|
||||
const UPDATE_COMPLETED = Symbol();
|
||||
|
||||
const defaultState = Immutable.fromJS({
|
||||
profile: {},
|
||||
updated: false,
|
||||
updateSuccess: false,
|
||||
fetchSuccess: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
/** ********************************************
|
||||
** User States **
|
||||
******************************************** */
|
||||
|
||||
class State {
|
||||
static FetchUser(error, user) {
|
||||
return {
|
||||
type: error ? FETCH_USER_ERR : FETCH_USER,
|
||||
user: error ? undefined : user,
|
||||
error: error || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
static UpdateUser(error) {
|
||||
return {
|
||||
type: error ? UPDATE_USER_ERR : UPDATE_USER_SUCCESS,
|
||||
error: error || undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** ********************************************
|
||||
** Actions **
|
||||
******************************************** */
|
||||
|
||||
const FetchUser = () => async (dispatch) => {
|
||||
try {
|
||||
const response = await fetch(config.apiRoutes.API_URL + config.apiRoutes.user.user, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${storage.get("token")}`,
|
||||
},
|
||||
});
|
||||
|
||||
const status = await response.status;
|
||||
if (status === 401 || status === 403) {
|
||||
return dispatch(LogoutUser());
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!data) throw new Error("Empty response from server");
|
||||
if (data.error) throw new Error(data.error.message);
|
||||
|
||||
dispatch(State.FetchUser(null, data.user));
|
||||
} catch (err) {
|
||||
dispatch(State.FetchUser(err.message));
|
||||
}
|
||||
};
|
||||
|
||||
const UpdateUser = (user) => async (dispatch) => {
|
||||
try {
|
||||
const response = await fetch(config.apiRoutes.API_URL + config.apiRoutes.user.user, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${storage.get("token")}`,
|
||||
},
|
||||
body: JSON.stringify({ user }),
|
||||
});
|
||||
|
||||
const status = await response.status;
|
||||
if (status === 401 || status === 403) {
|
||||
return dispatch(LogoutUser());
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!data) throw new Error("Empty response from server");
|
||||
if (data.error) throw new Error(data.error.message);
|
||||
|
||||
dispatch(State.FetchUser(null, data.user));
|
||||
dispatch(State.UpdateUser());
|
||||
} catch (err) {
|
||||
dispatch(State.UpdateUser(err.message));
|
||||
}
|
||||
};
|
||||
|
||||
/** ********************************************
|
||||
** User Reducer **
|
||||
******************************************** */
|
||||
|
||||
const User = (state = defaultState, action) => {
|
||||
switch (action.type) {
|
||||
case FETCH_USER:
|
||||
return state.withMutations((val) => {
|
||||
val.set("error", null);
|
||||
val.set("profile", action.user);
|
||||
val.set("fetchSuccess", true);
|
||||
});
|
||||
|
||||
case FETCH_USER_ERR:
|
||||
return state.withMutations((val) => {
|
||||
val.set("error", action.error);
|
||||
val.set("profile", {});
|
||||
val.set("fetchSuccess", false);
|
||||
});
|
||||
|
||||
case UPDATE_USER_ERR:
|
||||
return state.withMutations((val) => {
|
||||
val.set("error", action.error);
|
||||
val.set("updated", true);
|
||||
val.set("updateSuccess", false);
|
||||
});
|
||||
|
||||
case UPDATE_USER_SUCCESS:
|
||||
return state.withMutations((val) => {
|
||||
val.set("error", null);
|
||||
val.set("updated", true);
|
||||
val.set("updateSuccess", true);
|
||||
});
|
||||
|
||||
case UPDATE_COMPLETED:
|
||||
return state.withMutations((val) => {
|
||||
val.set("updated", false);
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const UserUpdateDone = () => ({ type: UPDATE_COMPLETED });
|
||||
export { User, FetchUser, UpdateUser, UserUpdateDone };
|
17
client/src/utils/displayUserType.js
Normal file
17
client/src/utils/displayUserType.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
export const getDisplayUserType = (userType) => {
|
||||
let displayUserType;
|
||||
switch (userType) {
|
||||
case "staff":
|
||||
displayUserType = "Staff Member";
|
||||
break;
|
||||
case "medical":
|
||||
displayUserType = "Medical Staff";
|
||||
break;
|
||||
case "nurse":
|
||||
displayUserType = "Charge Nurse";
|
||||
break;
|
||||
default:
|
||||
displayUserType = "";
|
||||
}
|
||||
return displayUserType;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue