diff --git a/frontend/src/App.js b/frontend/src/App.js index 328aa4de96d6a4bff3507f77c9f2377255425437..31131fd2d909cc0a238b6e04266e4e0da03f689a 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -17,7 +17,7 @@ function App() { <Switch> <Route path="/" exact component={Startseite} /> <Route path="/login" exact component={Startseite} /> - <Route path="/apotheke/:id" exact component={BTMBuch} /> + <Route path="/apotheke/:apoId" exact component={BTMBuch} /> <Route path="/apotheke/:apoId/einstellungen" exact component={ApothekeEinstellungen} /> </Switch> </Router> diff --git a/frontend/src/components/apotheke/ApothekeBtmList.js b/frontend/src/components/apotheke/ApothekeBtmList.js index 32b0a923c20b4248c253714a3d8585b06e594fdd..ae96232c37c22855728fecc76a96de23e79bc052 100644 --- a/frontend/src/components/apotheke/ApothekeBtmList.js +++ b/frontend/src/components/apotheke/ApothekeBtmList.js @@ -1,14 +1,16 @@ import React, { useState, useEffect } from "react"; -import { FormControl } from "react-bootstrap"; +import { useParams } from 'react-router-dom'; import BuchungTabelle from "../btmbuch/BuchungTabelle"; function ApothekeBtmList(props) { + + const { apoId } = useParams(); const [btms, setBtms] = useState([]); const [input, setInput] = useState(""); const getBtms = async () => { const response = await fetch( - `http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${props.match.params.id}/btmbuchung`, + `http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${apoId}/btmbuchung`, { method: "GET", headers: { diff --git a/frontend/src/components/apotheke/ApothekenDetails.js b/frontend/src/components/apotheke/ApothekenDetails.js index becdb08d5f464abe6fac2cb2e6e0781cfaa0db05..892d22bdd520ec41b98d13ed22086c5665af69f9 100644 --- a/frontend/src/components/apotheke/ApothekenDetails.js +++ b/frontend/src/components/apotheke/ApothekenDetails.js @@ -1,15 +1,18 @@ -import React, { Fragment, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; +import { useParams } from 'react-router-dom'; import { Button } from "react-bootstrap"; import NeuesBtmModal from '../btmbuch/NeuesBtmModal'; import '../../App.scss' function ApothekenDetails(props) { - const[apotheke, setApotheke] = useState({anschrift:{}}); - const[neuesBtmModalShow, setneuesBtmModalShow] = useState(false); + + const { apoId } = useParams(); + const[apotheke, setApotheke] = useState({anschrift:{}}); + const[neuesBtmModalShow, setneuesBtmModalShow] = useState(false); const getApothekeData = async () => { - const response = await fetch(`http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${props.match.params.id}`, { + const response = await fetch(`http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${apoId}`, { method: 'GET', headers: { 'Authorization': 'Bearer ' + window.sessionStorage.getItem("edbapo-jwt"), diff --git a/frontend/src/components/btmbuch/BTMBuch.js b/frontend/src/components/btmbuch/BTMBuch.js index b2434313bebf92cd6d8dd5873f667f38eb8b7908..004f8bde168eca4d032c6dcfbaefa92319488a36 100644 --- a/frontend/src/components/btmbuch/BTMBuch.js +++ b/frontend/src/components/btmbuch/BTMBuch.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; import Header from '../headers/Header' import StatusHeader from '../headers/StatusHeader' import ApothekenDetails from '../apotheke/ApothekenDetails'; @@ -8,9 +9,8 @@ import {Row, Col} from 'react-bootstrap'; import './BTMBuch.scss' function BTMBuch (props) { - let paths = props.location.pathname.split("/"); + const { apoId } = useParams(); - const [apothekeId, setApothekeId] = useState(paths[paths.length-1]) const [user, setUser] = useState({}); const [isLoggedIn, setLoggedIn] = useState(false); const [aktiveRolle, setAktiveRolle] = useState(''); @@ -51,10 +51,10 @@ function BTMBuch (props) { {aktiveRolle.toLowerCase() !== 'benutzer' ?<StatusHeader aktiveRolle={aktiveRolle}/> : null} <Header /> <Row className="details-list"> - <Col><ApothekenDetails {...props} apothekeRefFunctions={apothekeRefFunctions} apothekeId={apothekeId}/></Col> - <Col>{isLoggedIn ? <UserDetails {...props} user={user} aktiveRolle={aktiveRolle} setAktiveRolle={setAktiveRolle}/> : null }</Col> + <Col><ApothekenDetails {...props} apothekeRefFunctions={apothekeRefFunctions} apothekeId={apoId}/></Col> + <Col>{isLoggedIn ? <UserDetails {...props} user={user} setUser={setUser} aktiveRolle={aktiveRolle} setAktiveRolle={setAktiveRolle}/> : null }</Col> </Row> - <ApothekeBtmList apothekeId={apothekeId} user={user} apothekeRefFunctions={apothekeRefFunctions} {...props}/> + <ApothekeBtmList apothekeId={apoId} user={user} apothekeRefFunctions={apothekeRefFunctions} {...props}/> </React.Fragment> ) } diff --git a/frontend/src/modals/UserDetailsUpdateModal.js b/frontend/src/modals/UserDetailsUpdateModal.js new file mode 100644 index 0000000000000000000000000000000000000000..19fbd6d30482f9f25cbc5446ff77052eb81514fb --- /dev/null +++ b/frontend/src/modals/UserDetailsUpdateModal.js @@ -0,0 +1,183 @@ +import React, { useState, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { Modal, Col, Button, Form } from 'react-bootstrap'; +import { useSnackbar } from 'notistack'; + +function UserDetailsUpdateModal(props) { + + const { apoId } = useParams(); + let { nutzername, name, vorname, id } = props.user; + const [nutzernameVergeben, setNutzernameVergeben] = useState(false); + + //for password checking + const [passwordConfirmInvalid, setPasswordConfirmInvalid] = useState(false); + const [newPasswordVal, setNewPasswordVal] = useState(''); + const [passwordConfirmVal, setPasswordConfirmVal] = useState(''); + + const { enqueueSnackbar } = useSnackbar(); + + + const updateDetails = async event => { + event.preventDefault(); + let { username, vorname, nachname, neuesPasswort, altesPasswort } = event.target; + let body = { + name: nachname.value, + nutzername: username.value, + vorname: vorname.value, + oldPassword: altesPasswort.value, + aktiv: true + } + if(neuesPasswort.value){ + body.newPassword = neuesPasswort.value; + } + const response = await fetch(`http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${apoId}/benutzer/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + window.sessionStorage.getItem("edbapo-jwt"), + }, + body: JSON.stringify(body) + }).catch((err) => { + console.log(err); + return; + }); + + if(response.status === 200) { + const data = await response.json(); + props.onHide(); + props.setUser(data); + let pw = neuesPasswort.value ? neuesPasswort.value: altesPasswort.value; + if(props.loggedInUser.nutzername === nutzername) { + //if the same user is logged in and changes its details a new token is needed + getNewJwt(username.value, pw); + }else { + enqueueSnackbar('Benutzer Informationen aktualisiert', { variant:'success', autoHideDuration: 3000} ); + } + }else if(response.status === 400) { + enqueueSnackbar('Ein Fehler ist aufgetaucht', { variant:'error', autoHideDuration: 3000} ); + }else if(response.status === 403) { + enqueueSnackbar('Falsches Passwort', { variant:'error', autoHideDuration: 3000} ); + } + } + + const getNewJwt = async (username, password) => { + console.log(username, password) + const response = await fetch(`http://${process.env.REACT_APP_BACKEND_URL}/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: username, + password: password + }) + }).catch((err) => { + //SHOW ERROR + console.log(err); + }); + + if(response && response.status === 200){ + const data = await response.json(); + window.sessionStorage.setItem("edbapo-jwt", data.jwt) + enqueueSnackbar('Benutzer Informationen aktualisiert', { variant:'success', autoHideDuration: 3000} ); + }else { + enqueueSnackbar('Ein Fehler ist aufgetaucht', { variant:'error', autoHideDuration: 3000} ); + } + } + + + const checkIfUserNameIsTaken = async event => { + let newUsername = event.target.value; + if (newUsername.length < 4) { + //if shorter than 4 its invalid + setNutzernameVergeben(true); + return; + } + + if (newUsername !== nutzername && newUsername) { + const response = await fetch(`http://${process.env.REACT_APP_BACKEND_URL}/apotheke/${apoId}/benutzer/${newUsername}/checkUsername`, { + method: 'POST', + }).catch((err) => { + console.log(err); + return; + }); + + if (response.status === 200) { + setNutzernameVergeben(false); + } else if (response.status === 400) { + setNutzernameVergeben(true); + } + } else { + setNutzernameVergeben(false); + } + } + + useEffect(() => { + setPasswordConfirmInvalid(newPasswordVal !== passwordConfirmVal); + }, [newPasswordVal, passwordConfirmVal]); + + + return ( + <Modal + {...props} + size="lg" + aria-labelledby="contained-modal-title-vcenter" + centered + onExiting={props.onHide} + > + <Modal.Header closeButton> + <Modal.Title id="contained-modal-title-vcenter"> + Benutzer Einstellungen + </Modal.Title> + </Modal.Header> + <Form onSubmit={updateDetails}> + <Modal.Body> + <Form.Row> + <Form.Group as={Col} controlId="username"> + <Form.Label>Benutzername</Form.Label> + <Form.Control name="username" required onChange={checkIfUserNameIsTaken} + isInvalid={nutzernameVergeben} defaultValue={nutzername} type="text" /> + </Form.Group> + </Form.Row> + + <Form.Row> + <Form.Group as={Col} sm={4} controlId="vorname"> + <Form.Label>Vorname</Form.Label> + <Form.Control name="vorname" required defaultValue={vorname} type="text" /> + </Form.Group> + + <Form.Group as={Col} sm={8} controlId="nachname"> + <Form.Label>Nachname</Form.Label> + <Form.Control name="nachname" required defaultValue={name} type="text" /> + </Form.Group> + </Form.Row> + + <Form.Row> + <Form.Group as={Col} controlId="neuesPasswort"> + <Form.Label>Neues Passwort</Form.Label> + <Form.Control onChange={event => setNewPasswordVal(event.target.value)} name="neuesPasswort" type="password" /> + </Form.Group> + <Form.Group as={Col} controlId="neuesPasswortConfirm"> + <Form.Label>Neues Passwort bestätigen</Form.Label> + <Form.Control onChange={event => setPasswordConfirmVal(event.target.value)} isInvalid={passwordConfirmInvalid} name="neuesPasswortConfirm" type="password" /> + </Form.Group> + </Form.Row> + + <hr /> + <Form.Row> + <Form.Group as={Col} controlId="altesPasswort"> + <Form.Label>Passwort eingeben</Form.Label> + <Form.Control name="altesPasswort" required type="password" /> + </Form.Group> + </Form.Row> + </Modal.Body> + <Modal.Footer> + <Button autofocus variant="" onClick={props.onHide}>Abbrechen</Button> + <Button variant="primary" type="submit" >Bestätigen</Button> + </Modal.Footer> + </Form> + </Modal> + ) +} + +export default UserDetailsUpdateModal; \ No newline at end of file diff --git a/frontend/src/user/UserDetails.js b/frontend/src/user/UserDetails.js index d3fc55af5b8f0e712160299b84343aecc049b68f..2d2fc2b9c19a11de8bf83172b84bc8a8c1bdcc8c 100644 --- a/frontend/src/user/UserDetails.js +++ b/frontend/src/user/UserDetails.js @@ -1,9 +1,12 @@ -import React from 'react' +import React, {useState} from 'react' import { Button, Col, Row, Form } from 'react-bootstrap'; +import { Settings } from '@material-ui/icons'; +import UserDetailsUpdateModal from '../modals/UserDetailsUpdateModal'; function UserDetails(props) { //eslint disable-next-line const {rolle, vorname, name, nutzername, aktiv} = props.user; + const [showUserSettings, setShowUserSettings] = useState(false); var allRoles = { ADMIN : { 0: "Admin", 1 : "Pruefer", 2: "Benutzer"}, @@ -18,6 +21,8 @@ function UserDetails(props) { return( <Row> + <UserDetailsUpdateModal {...props} loggedInUser={props.user} show={showUserSettings} onHide={() => setShowUserSettings(false)}/> + <Col><b>Nutzername:</b> {nutzername}</Col> <Col> <Form.Control as="select" onChange={(event) => props.setAktiveRolle(event.target.value.toUpperCase())}> @@ -25,7 +30,10 @@ function UserDetails(props) { </Form.Control> </Col> <Col> - <Button>Einstellungen</Button> + <Button onClick={() => setShowUserSettings(true)}> + <Settings /> + Einstellungen + </Button> </Col> <Col> <Button onClick={logout}>Logout</Button>