├── .env ├── .gitignore ├── README.md ├── client ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── actions │ ├── alert.js │ ├── appointment.js │ ├── authDoctor.js │ ├── authUser.js │ ├── profile.js │ ├── review.js │ └── types.js │ ├── components │ ├── auth │ │ ├── DoctorRegister.js │ │ ├── LoginDoctor.js │ │ ├── LoginUser.js │ │ └── UserRegister.js │ ├── bookappointment │ │ ├── AppointmentForm.js │ │ └── Form.js │ ├── dashboard │ │ ├── Dashboard.js │ │ ├── Education.js │ │ ├── Experience.js │ │ ├── Graph.js │ │ ├── Patient.js │ │ └── Review.js │ ├── layout │ │ ├── Alert.js │ │ ├── Landing.js │ │ ├── Navbar.js │ │ ├── Spinner.js │ │ └── doctor (1).png │ ├── profile-forms │ │ ├── AddEducation.js │ │ ├── AddExperience.js │ │ ├── CreateProfile.js │ │ └── EditProfile.js │ ├── profile │ │ ├── Profile.js │ │ ├── ProfileAbout.js │ │ ├── ProfileEducation.js │ │ ├── ProfileExperience.js │ │ ├── ProfileReview.js │ │ ├── ProfileReviewForm.js │ │ ├── ProfileTop.js │ │ └── ReviewForm.js │ ├── profiles │ │ ├── ProfileItem.js │ │ └── Profiles.js │ ├── routing │ │ ├── PrivateDoctorRoute.js │ │ └── PrivateUserRoute.js │ └── user │ │ ├── AppointmentItems.js │ │ └── Appointments.js │ ├── img │ ├── calendar.svg │ ├── coughing_2.svg │ ├── doctor.svg │ ├── doctor4.svg │ ├── doctor8.svg │ ├── graduation.svg │ ├── mention.svg │ ├── newDoctor1.svg │ ├── undraw_account_490v.svg │ ├── undraw_doctor_kw5l.svg │ ├── undraw_forms_78yw.svg │ └── undraw_medical_research_qg4d.svg │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── reducers │ ├── alert.js │ ├── appointment.js │ ├── authDoctor.js │ ├── authUser.js │ ├── index.js │ ├── profile.js │ └── review.js │ ├── store.js │ └── utils │ └── setAuthToken.js ├── config └── keys.js ├── middleware ├── authDoctor.js └── authUser.js ├── models ├── Doctor.js ├── Profile.js └── User.js ├── package-lock.json ├── package.json ├── routes └── api │ ├── appointment.js │ ├── authDoctor.js │ ├── authUser.js │ ├── doctors.js │ ├── profile.js │ └── users.js └── server.js /.env: -------------------------------------------------------------------------------- 1 | jwtSecret=mysecrettoken -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Doctor-Assigned 2 | > Doctor appointment booking for patients 3 | 4 | This is a MERN stack application which is based on booking appointment which includes doctor authentication, create profile,add experience, add education, dashboard with recent appointments, recent reviews and user authentication, appointment booking and cancel appointment, post review and delete review to a doctor profile by users. 5 | #### [Click Here](https://drive.google.com/drive/folders/1BplDurTpvfIIzYjeMv98YfslDRjVdf_e?usp=sharing) for screenshots 6 | 7 | ## Install server dependencies 8 | `npm install` 9 | 10 | ## Install client dependencies 11 | `cd client` 12 | 13 | `npm install` 14 | 15 | ## Run both Express & React from root 16 | `npm run dev` 17 | 18 | 19 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # misc 7 | /.pnp 8 | .pnp.js 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "axios": "^0.19.2", 10 | "chart.js": "^2.9.3", 11 | "moment": "^2.27.0", 12 | "react": "^16.13.1", 13 | "react-chartjs-2": "^2.9.0", 14 | "react-dom": "^16.13.1", 15 | "react-moment": "^0.9.7", 16 | "react-preloaders": "^3.0.3", 17 | "react-redux": "^7.2.0", 18 | "react-router-dom": "^5.2.0", 19 | "react-scripts": "3.4.1", 20 | "redux-devtools-extension": "^2.13.8", 21 | "redux-thunk": "^2.3.0", 22 | "uuid": "^8.2.0" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test": "react-scripts test", 28 | "eject": "react-scripts eject" 29 | }, 30 | "eslintConfig": { 31 | "extends": "react-app" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "proxy": "http://localhost:5000" 46 | } 47 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeevantheDev/Doctor-Assigned/87f6c1cb7e814e0c5c628d1e5393a227c9719487/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | React App 19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeevantheDev/Doctor-Assigned/87f6c1cb7e814e0c5c628d1e5393a227c9719487/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeevantheDev/Doctor-Assigned/87f6c1cb7e814e0c5c628d1e5393a227c9719487/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, Fragment } from 'react'; 2 | import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; 3 | import Alert from './components/layout/Alert'; 4 | import Navbar from './components/layout/Navbar'; 5 | import Landing from './components/layout/Landing'; 6 | import LoginUser from './components/auth/LoginUser'; 7 | import LoginDoctor from './components/auth/LoginDoctor'; 8 | import DoctorRegister from './components/auth/DoctorRegister'; 9 | import UserRegister from './components/auth/UserRegister'; 10 | import Dashboard from './components/dashboard/Dashboard'; 11 | import AddEducation from './components/profile-forms/AddEducation'; 12 | import AddExperience from './components/profile-forms/AddExperience'; 13 | import CreateProfile from './components/profile-forms/CreateProfile'; 14 | import EditProfile from './components/profile-forms/EditProfile'; 15 | import Profiles from './components/profiles/Profiles'; 16 | import Profile from './components/profile/Profile'; 17 | import PrivateDoctorRoute from './components/routing/PrivateDoctorRoute'; 18 | import Appointment from './components/user/Appointments'; 19 | import AppointmentForm from './components/bookappointment/AppointmentForm'; 20 | import PrivateUserRoute from './components/routing/PrivateUserRoute'; 21 | import './App.css'; 22 | 23 | // Redux 24 | import {Provider} from 'react-redux'; 25 | import store from './store'; 26 | import { loadUser } from './actions/authUser'; 27 | import { loadDoctor } from './actions/authDoctor'; 28 | import setAuthToken from './utils/setAuthToken'; 29 | 30 | 31 | if(localStorage.token) { 32 | setAuthToken(localStorage.token); 33 | } 34 | 35 | const App = () => { 36 | useEffect(() => { 37 | store.dispatch(loadUser()); 38 | store.dispatch(loadDoctor()); 39 | }, []); 40 | 41 | return ( 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 |
66 |
67 |
68 | ); 69 | } 70 | 71 | export default App; 72 | -------------------------------------------------------------------------------- /client/src/actions/alert.js: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid'; 2 | import { SET_ALERT, REMOVE_ALERT } from './types'; 3 | 4 | export const setAlert = (msg, alertType, timeOut=5000) => dispatch => { 5 | const id = uuidv4(); 6 | dispatch({ 7 | type: SET_ALERT, 8 | payload: { msg, alertType, id } 9 | }); 10 | 11 | setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeOut); 12 | }; -------------------------------------------------------------------------------- /client/src/actions/appointment.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import {setAlert} from './alert'; 3 | 4 | import { 5 | GET_APPOINTMENTS, 6 | ADD_APPOINTMENTS, 7 | UPDATE_APPOINTMENTS, 8 | APPOINTMENT_ERROR, 9 | DELETE_USER_ACCOUNT 10 | } from './types'; 11 | 12 | // Get Appointments 13 | export const getAppointments = () => async dispatch => { 14 | try { 15 | const res = await axios.get('/api/authUser'); 16 | dispatch({ 17 | type: GET_APPOINTMENTS, 18 | payload: res.data 19 | }); 20 | 21 | } catch (err) { 22 | dispatch({ 23 | type: APPOINTMENT_ERROR, 24 | payload: {msg: err.response.statusText, status: err.response.status} 25 | }); 26 | }; 27 | }; 28 | 29 | // Add appointment 30 | export const addAppointment = (doctorId, formData, history) => async dispatch => { 31 | const config = { 32 | headers: { 33 | 'Content-Type': 'application/json' 34 | } 35 | }; 36 | try { 37 | const res = await axios.post(`/api/appointment/${doctorId}`,formData, config); 38 | dispatch({ 39 | type: ADD_APPOINTMENTS, 40 | payload: res.data 41 | }); 42 | 43 | dispatch(setAlert('Appointment booked', 'success')); 44 | history.push('/appointment'); 45 | 46 | } catch (err) { 47 | dispatch({ 48 | type: APPOINTMENT_ERROR, 49 | payload: { msg: err.response.statusText, status: err.response.status } 50 | }); 51 | }; 52 | }; 53 | 54 | // Delete appointment 55 | export const deleteAppointment = (appointmentId) => async dispatch => { 56 | try { 57 | const res = await axios.delete(`/api/authUser/${appointmentId}`); 58 | dispatch({ 59 | type: UPDATE_APPOINTMENTS, 60 | payload: res.data 61 | }); 62 | 63 | dispatch(setAlert('Appointment removed', 'success')); 64 | } catch (err) { 65 | dispatch({ 66 | type: APPOINTMENT_ERROR, 67 | payload: { msg: err.response.statusText, status: err.response.status } 68 | }); 69 | }; 70 | }; 71 | 72 | // Delete user account 73 | export const deleteAccountUser = () => async dispatch => { 74 | if(window.confirm('Are you sure? this can not be undone!')) { 75 | try { 76 | await axios.delete('/api/authUser'); 77 | 78 | dispatch({ 79 | type: DELETE_USER_ACCOUNT 80 | }); 81 | 82 | dispatch(setAlert("Your account has been removed", 'success')); 83 | } catch (err) { 84 | dispatch ({ 85 | type: APPOINTMENT_ERROR, 86 | payload: { msg: err.response.statusText, status: err.response.status } 87 | }); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /client/src/actions/authDoctor.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { setAlert } from './alert'; 3 | import { 4 | REGISTER_DOCTOR_SUCCESS, 5 | REGISTER_DOCTOR_FAIL, 6 | DOCTOR_LOADED, 7 | AUTH_DOCTOR_ERROR, 8 | LOGIN_DOCTOR_SUCCESS, 9 | LOGIN_DOCTOR_FAIL, 10 | LOGOUT_DOCTOR, 11 | CLEAR_PROFILE 12 | } from './types'; 13 | import setAuthToken from '../utils/setAuthToken'; 14 | 15 | // Load Doctors 16 | export const loadDoctor = () => async dispatch => { 17 | if(localStorage.token) { 18 | setAuthToken(localStorage.token); 19 | } 20 | 21 | try { 22 | const res = await axios.get('/api/authDoctor'); 23 | 24 | dispatch({ 25 | type: DOCTOR_LOADED, 26 | payload: res.data 27 | }); 28 | } catch (err) { 29 | dispatch({ 30 | type: AUTH_DOCTOR_ERROR 31 | }); 32 | } 33 | }; 34 | 35 | // Register Doctor 36 | export const register = ({ name, email, password }) => async dispatch => { 37 | const config = { 38 | headers: { 39 | 'Content-Type': 'application/json' 40 | } 41 | } 42 | const body = JSON.stringify({ name, email, password }); 43 | try { 44 | const res = await axios.post('/api/doctors', body, config); 45 | console.log(res); 46 | 47 | dispatch({ 48 | type: REGISTER_DOCTOR_SUCCESS, 49 | payload: res.data 50 | }); 51 | dispatch(loadDoctor()); 52 | } catch (err) { 53 | const errors = err.response.data.errors; 54 | if(errors) { 55 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 56 | } 57 | 58 | dispatch({ 59 | type: REGISTER_DOCTOR_FAIL 60 | }); 61 | } 62 | }; 63 | 64 | // Login Doctor 65 | export const login = (email, password) => async (dispatch) => { 66 | const config = { 67 | headers: { 68 | 'Content-Type': 'application/json' 69 | } 70 | } 71 | const body = JSON.stringify({ email, password }); 72 | try { 73 | const res = await axios.post('/api/authDoctor', body, config); 74 | 75 | dispatch({ 76 | type: LOGIN_DOCTOR_SUCCESS, 77 | payload: res.data 78 | }); 79 | dispatch(loadDoctor()); 80 | } catch (err) { 81 | const errors = err.response.data.errors; 82 | if(errors) { 83 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 84 | } 85 | 86 | dispatch({ 87 | type: LOGIN_DOCTOR_FAIL 88 | }); 89 | } 90 | }; 91 | 92 | // Logout/ clear Profile 93 | export const logout_doctor = () => dispatch => { 94 | dispatch({ 95 | type: CLEAR_PROFILE 96 | }); 97 | dispatch({ 98 | type: LOGOUT_DOCTOR 99 | }); 100 | } -------------------------------------------------------------------------------- /client/src/actions/authUser.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { setAlert } from './alert'; 3 | import { 4 | REGISTER_USER_SUCCESS, 5 | REGISTER_USER_FAIL, 6 | USER_LOADED, 7 | AUTH_USER_ERROR, 8 | LOGIN_USER_SUCCESS, 9 | LOGIN_USER_FAIL, 10 | LOGOUT_USER, 11 | CLEAR_USER_PROFILE 12 | } from './types'; 13 | import setAuthToken from '../utils/setAuthToken'; 14 | 15 | // Load User 16 | export const loadUser = () => async dispatch => { 17 | if(localStorage.token) { 18 | setAuthToken(localStorage.token); 19 | } 20 | 21 | try { 22 | const res = await axios.get('/api/authUser'); 23 | 24 | dispatch({ 25 | type: USER_LOADED, 26 | payload: res.data 27 | }); 28 | } catch (err) { 29 | dispatch({ 30 | type: AUTH_USER_ERROR 31 | }); 32 | } 33 | }; 34 | 35 | // Register User 36 | export const register = ({ name, email, password }) => async dispatch => { 37 | const config = { 38 | headers: { 39 | 'Content-Type': 'application/json' 40 | } 41 | } 42 | const body = JSON.stringify({ name, email, password }); 43 | try { 44 | const res = await axios.post('./api/users', body, config); 45 | 46 | dispatch({ 47 | type: REGISTER_USER_SUCCESS, 48 | payload: res.data 49 | }); 50 | dispatch(loadUser()); 51 | } catch (err) { 52 | const errors = err.response.data.errors; 53 | if(errors) { 54 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 55 | } 56 | 57 | dispatch({ 58 | type: REGISTER_USER_FAIL 59 | }); 60 | } 61 | }; 62 | 63 | // Login User 64 | export const login = (email, password) => async (dispatch) => { 65 | const config = { 66 | headers: { 67 | 'Content-Type': 'application/json' 68 | } 69 | } 70 | const body = JSON.stringify({ email, password }); 71 | try { 72 | const res = await axios.post('/api/authUser', body, config); 73 | 74 | dispatch({ 75 | type: LOGIN_USER_SUCCESS, 76 | payload: res.data 77 | }); 78 | dispatch(loadUser()); 79 | } catch (err) { 80 | const errors = err.response.data.errors; 81 | if(errors) { 82 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 83 | } 84 | 85 | dispatch({ 86 | type: LOGIN_USER_FAIL 87 | }); 88 | } 89 | }; 90 | 91 | // Logout/ clear Profile 92 | export const logout_user = () => dispatch => { 93 | dispatch({ 94 | type: CLEAR_USER_PROFILE 95 | }); 96 | dispatch({ 97 | type: LOGOUT_USER 98 | }); 99 | } -------------------------------------------------------------------------------- /client/src/actions/profile.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import {setAlert} from './alert'; 3 | 4 | import { 5 | GET_PROFILE, 6 | GET_PROFILE_BY_ID, 7 | GET_PROFILES, 8 | UPDATE_PROFILE, 9 | PROFILE_ERROR, 10 | CLEAR_PROFILE, 11 | ADD_REVIEW, 12 | ADD_REVIEW_ERROR, 13 | REMOVE_REVIEW, 14 | DELETE_ACCOUNT 15 | } from './types'; 16 | 17 | // Get current doctors profile 18 | export const getCurrentProfile = () => async dispatch => { 19 | try { 20 | const res = await axios.get('/api/profile/me'); 21 | dispatch({ 22 | type: GET_PROFILE, 23 | payload: res.data 24 | }); 25 | } catch (err) { 26 | dispatch({ 27 | type: PROFILE_ERROR, 28 | payload: { msg: err.response.statusText, status: err.response.status } 29 | }); 30 | 31 | } 32 | }; 33 | // Get all profiles 34 | export const getProfiles = () => async dispatch => { 35 | try { 36 | const res = await axios.get('/api/profile'); 37 | 38 | dispatch({ 39 | type: GET_PROFILES, 40 | payload: res.data 41 | }); 42 | } catch (err) { 43 | dispatch({ 44 | type: PROFILE_ERROR, 45 | payload: { msg: err.response.statusText, status: err.response.status } 46 | }); 47 | } 48 | }; 49 | 50 | // Get profiles by doctor id 51 | export const getProfileById = doctorId => async dispatch => { 52 | try { 53 | const res = await axios.get(`/api/profile/doctor/${doctorId}`); 54 | dispatch({ 55 | type: GET_PROFILE_BY_ID, 56 | payload: res.data 57 | }); 58 | } catch (err) { 59 | dispatch({ 60 | type: PROFILE_ERROR, 61 | payload: { msg: err.response.statusText, status: err.response.status } 62 | }); 63 | } 64 | }; 65 | 66 | // Create / Update a profile 67 | export const createProfile = (formData, history, edit=false) => async dispatch => { 68 | try { 69 | const config = { 70 | headers: { 71 | 'Content-Type': 'application/json' 72 | } 73 | } 74 | const res = await axios.post('api/profile', formData, config); 75 | dispatch({ 76 | type: GET_PROFILE, 77 | payload: res.data 78 | }); 79 | dispatch(setAlert(edit ? 'Profile Updated' : 'Profile Created', 'success')); 80 | 81 | if(!edit) { 82 | history.push('/dashboard'); 83 | } 84 | } catch (err) { 85 | const errors = err.response.data.errors; 86 | if(errors) { 87 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 88 | } 89 | 90 | dispatch({ 91 | type: PROFILE_ERROR, 92 | payload: { msg: err.response.statusText, status: err.response.status } 93 | }); 94 | } 95 | }; 96 | 97 | // ADD Experience 98 | export const addExperience = (formData, history) => async dispatch => { 99 | try { 100 | const config = { 101 | headers: { 102 | 'Content-Type': 'application/json' 103 | } 104 | } 105 | const res = await axios.put('api/profile/experience', formData, config); 106 | dispatch({ 107 | type: UPDATE_PROFILE, 108 | payload: res.data 109 | }); 110 | dispatch(setAlert("Experience added", 'success')); 111 | history.push('/dashboard'); 112 | } catch (err) { 113 | const errors = err.response.data.errors; 114 | if(errors) { 115 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 116 | } 117 | 118 | dispatch({ 119 | type: PROFILE_ERROR, 120 | payload: { msg: err.response.statusText, status: err.response.status } 121 | }); 122 | } 123 | }; 124 | // ADD Education 125 | export const addEducation = (formData, history) => async dispatch => { 126 | try { 127 | const config = { 128 | headers: { 129 | 'Content-Type': 'application/json' 130 | } 131 | } 132 | const res = await axios.put('api/profile/education', formData, config); 133 | dispatch({ 134 | type: UPDATE_PROFILE, 135 | payload: res.data 136 | }); 137 | dispatch(setAlert("Education added", 'success')); 138 | console.log(history); 139 | 140 | history.push('/dashboard'); 141 | } catch (err) { 142 | const errors = err.response.data.errors; 143 | if(errors) { 144 | errors.forEach(error => dispatch(setAlert(error.msg, 'danger'))); 145 | } 146 | 147 | dispatch({ 148 | type: PROFILE_ERROR, 149 | payload: { msg: err.response.statusText, status: err.response.status } 150 | }); 151 | } 152 | }; 153 | 154 | // Delete experience 155 | export const deleteExperience = exp_id => async dispatch => { 156 | try { 157 | const res = await axios.delete(`api/profile/experience/${exp_id}`); 158 | dispatch({ 159 | type: UPDATE_PROFILE, 160 | payload: res.data 161 | }); 162 | dispatch(setAlert("Experience Removed", 'success')); 163 | } catch (err) { 164 | dispatch ({ 165 | type: PROFILE_ERROR, 166 | payload: { msg: err.response.statusText, status: err.response.status } 167 | }); 168 | } 169 | }; 170 | // Delete education 171 | export const deleteEducation = edu_id => async dispatch => { 172 | try { 173 | const res = await axios.delete(`api/profile/education/${edu_id}`); 174 | dispatch({ 175 | type: UPDATE_PROFILE, 176 | payload: res.data 177 | }); 178 | dispatch(setAlert("Education Removed", 'success')); 179 | } catch (err) { 180 | dispatch ({ 181 | type: PROFILE_ERROR, 182 | payload: { msg: err.response.statusText, status: err.response.status } 183 | }); 184 | } 185 | }; 186 | 187 | // Add review 188 | export const addReview = (doctorId, formData) => async dispatch => { 189 | const config = { 190 | headers: { 191 | 'Content-Type': 'application/json' 192 | } 193 | }; 194 | try { 195 | const res = await axios.post(`/api/profile/doctor/:${doctorId}`, formData, config); 196 | dispatch({ 197 | type: ADD_REVIEW, 198 | payload: res.data 199 | }); 200 | 201 | dispatch(setAlert('Review Added', 'success')) 202 | } catch (err) { 203 | dispatch ({ 204 | type: ADD_REVIEW_ERROR, 205 | payload: { msg: err.response.statusText, status: err.response.status } 206 | }); 207 | } 208 | }; 209 | 210 | // Delete Review 211 | export const deleteReview = (doctorId, reviewId) => async dispatch => { 212 | try { 213 | await axios.delete(`/api/profile/doctor/${doctorId}/${reviewId}`); 214 | 215 | dispatch({ 216 | type: REMOVE_REVIEW, 217 | payload: reviewId 218 | }); 219 | 220 | dispatch(setAlert('Comment removed', 'success')); 221 | } catch (err) { 222 | dispatch ({ 223 | type: ADD_REVIEW_ERROR, 224 | payload: { msg: err.response.statusText, status: err.response.status } 225 | }); 226 | } 227 | }; 228 | 229 | // Delete account and profile 230 | export const deleteAccount = () => async dispatch => { 231 | if(window.confirm('Are you sure? this can not be undone!')) { 232 | try { 233 | await axios.delete('/api/profile'); 234 | 235 | dispatch({ 236 | type: CLEAR_PROFILE 237 | }); 238 | dispatch({ 239 | type: DELETE_ACCOUNT 240 | }); 241 | 242 | dispatch(setAlert("Your Account has been Removed", 'success')); 243 | } catch (err) { 244 | dispatch ({ 245 | type: PROFILE_ERROR, 246 | payload: { msg: err.response.statusText, status: err.response.status } 247 | }); 248 | } 249 | } 250 | }; 251 | -------------------------------------------------------------------------------- /client/src/actions/review.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import {setAlert} from './alert'; 3 | 4 | import { 5 | ADD_REVIEW, 6 | ADD_REVIEW_ERROR, 7 | REMOVE_REVIEW 8 | } from './types'; 9 | 10 | // Add review 11 | export const addReview = (doctorId, formData) => async dispatch => { 12 | const config = { 13 | headers: { 14 | 'Content-Type': 'application/json' 15 | } 16 | }; 17 | try { 18 | const res = await axios.post(`/api/profile/doctor/${doctorId}`, formData, config); 19 | 20 | dispatch({ 21 | type: ADD_REVIEW, 22 | payload: res.data.review 23 | }); 24 | 25 | dispatch(setAlert('Review Added', 'success')) 26 | } catch (err) { 27 | dispatch ({ 28 | type: ADD_REVIEW_ERROR, 29 | payload: { msg: err.response.statusText, status: err.response.status } 30 | }); 31 | } 32 | }; 33 | 34 | // Delete Review 35 | export const deleteReview = (doctorId, reviewId) => async dispatch => { 36 | try { 37 | await axios.delete(`/api/profile/doctor/${doctorId}/${reviewId}`); 38 | dispatch({ 39 | type: REMOVE_REVIEW, 40 | payload: reviewId 41 | }); 42 | 43 | dispatch(setAlert('Comment removed', 'success')); 44 | } catch (err) { 45 | dispatch ({ 46 | type: ADD_REVIEW_ERROR, 47 | payload: { msg: err.response.statusText, status: err.response.status } 48 | }); 49 | } 50 | }; -------------------------------------------------------------------------------- /client/src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const SET_ALERT = 'SET_ALERT'; 2 | export const REMOVE_ALERT = 'REMOVE_ALERT'; 3 | 4 | // Doctor action types 5 | export const REGISTER_DOCTOR_SUCCESS = 'REGISTER_DOCTOR_SUCCESS'; 6 | export const REGISTER_DOCTOR_FAIL = 'REGISTER_DOCTOR_FAIL'; 7 | export const DOCTOR_LOADED = 'DOCTOR_LOADED'; 8 | export const AUTH_DOCTOR_ERROR = 'AUTH_DOCTOR_ERROR'; 9 | export const LOGIN_DOCTOR_SUCCESS = 'LOGIN_DOCTOR_SUCCESS'; 10 | export const LOGIN_DOCTOR_FAIL = 'LOGIN_DOCTOR_FAIL'; 11 | export const LOGOUT_DOCTOR = 'LOGOUT_DOCTOR'; 12 | export const GET_PROFILE = 'GET_PROFILE'; 13 | export const GET_PROFILE_BY_ID = 'GET_PROFILE_BY_ID'; 14 | export const GET_PROFILES = 'GET_PROFILES'; 15 | export const UPDATE_PROFILE = 'UPDATE_PROFILE'; 16 | export const CLEAR_PROFILE = 'CLEAR_PROFILE'; 17 | export const PROFILE_ERROR = 'PROFILE_ERROR'; 18 | export const DELETE_ACCOUNT = 'DELETE_ACCOUNT'; 19 | 20 | // User action types 21 | export const REGISTER_USER_SUCCESS = 'REGISTER_USER_SUCCESS'; 22 | export const REGISTER_USER_FAIL = 'REGISTER_USER_FAIL'; 23 | export const USER_LOADED = 'USER_LOADED'; 24 | export const AUTH_USER_ERROR = 'AUTH_USER_ERROR'; 25 | export const LOGIN_USER_SUCCESS = 'LOGIN_USER_SUCCESS'; 26 | export const LOGIN_USER_FAIL = 'LOGIN_USER_FAIL'; 27 | export const LOGOUT_USER = 'LOGOUT_USER'; 28 | export const GET_APPOINTMENTS = 'GET_APPOINTMENTS'; 29 | export const ADD_APPOINTMENTS = 'ADD_APPOINTMENTS'; 30 | export const APPOINTMENT_ERROR = 'APPOINTMENT_ERROR'; 31 | export const UPDATE_APPOINTMENTS = 'UPDATE_APPOINTMENTS'; 32 | export const ADD_REVIEW = 'ADD_REVIEW'; 33 | export const ADD_REVIEW_ERROR = 'ADD_REVIEW_ERROR'; 34 | export const REMOVE_REVIEW = 'REMOVE_REVIEW'; 35 | export const CLEAR_USER_PROFILE = 'CLEAR_USER_PROFILE'; 36 | export const DELETE_USER_ACCOUNT = 'DELETE_USER_ACCOUNT'; -------------------------------------------------------------------------------- /client/src/components/auth/DoctorRegister.js: -------------------------------------------------------------------------------- 1 | import React, { useState,Fragment } from 'react'; 2 | import {Redirect} from 'react-router-dom'; 3 | import {setAlert} from '../../actions/alert'; 4 | import { register} from '../../actions/authDoctor'; 5 | import PropTypes from 'prop-types'; 6 | import {connect} from 'react-redux'; 7 | 8 | 9 | const DoctorRegister = ({ setAlert, register, isDoctorAuthenticated }) => { 10 | const [formData, setFormData] = useState({ 11 | name: '', 12 | email: '', 13 | password: '', 14 | password2: '' 15 | }); 16 | 17 | const {name, email, password, password2} = formData; 18 | const onChange = e => setFormData({ 19 | ...formData, 20 | [e.target.name]: e.target.value 21 | }); 22 | const onSubmit = async e => { 23 | e.preventDefault(); 24 | if(password !== password2) { 25 | setAlert('Password do not match', 'danger'); 26 | } else { 27 | register({ name, email, password }); 28 | } 29 | } 30 | if(isDoctorAuthenticated) { 31 | return 32 | } 33 | 34 | return ( 35 | 36 |
37 |
38 |
39 |
40 |
41 |

Doctor 42 | 43 |

44 |
45 |
onSubmit(e)}> 46 |
47 | 48 | onChange(e)} 55 | /> 56 | This site uses Gravatar so if you want a profile image, use a Gravatar email 57 |
58 |
59 | 60 | onChange(e)} 67 | /> 68 |
69 |
70 | 71 | onChange(e)} 78 | /> 79 |
80 |
81 | 82 | onChange(e)} 89 | /> 90 |
91 | 92 |
93 |
94 |
95 | 96 |
97 |
98 |
99 |
100 |
101 | ); 102 | }; 103 | 104 | DoctorRegister.propTypes = { 105 | setAlert: PropTypes.func.isRequired, 106 | register: PropTypes.func.isRequired, 107 | isDoctorAuthenticated: PropTypes.bool 108 | } 109 | 110 | const mapStateToProps =state => ({ 111 | isDoctorAuthenticated: state.authDoctor.isDoctorAuthenticated 112 | }); 113 | 114 | export default connect(mapStateToProps, {setAlert, register})(DoctorRegister); 115 | -------------------------------------------------------------------------------- /client/src/components/auth/LoginDoctor.js: -------------------------------------------------------------------------------- 1 | import React, { useState, Fragment } from 'react'; 2 | import {Link, Redirect} from 'react-router-dom'; 3 | import {connect} from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import {login} from '../../actions/authDoctor'; 6 | 7 | const LoginDoctor = ({ login, isDoctorAuthenticated }) => { 8 | const [formData, setFormData] = useState({ 9 | email: '', 10 | password: '' 11 | }); 12 | 13 | const {email, password} = formData; 14 | const onChange = e => setFormData({ 15 | ...formData, 16 | [e.target.name]: e.target.value 17 | }); 18 | const onSubmit = async e => { 19 | e.preventDefault(); 20 | login(email,password); 21 | } 22 | 23 | // Redirect if login 24 | if(isDoctorAuthenticated) { 25 | return 26 | } 27 | 28 | return ( 29 | 30 |
31 |
32 |
33 |
34 |
35 |

Log in Doctor 36 | 37 |

38 |
39 |
onSubmit(e)}> 40 |
41 | 42 | onChange(e)} 49 | required 50 | /> 51 | We'll never share your email with anyone else. 52 |
53 |
54 | 55 | onChange(e)} 63 | /> 64 |
65 | 66 |

or {' '} 67 | Create a new account

68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 |
76 |
77 | ); 78 | }; 79 | 80 | LoginDoctor.propTypes ={ 81 | login: PropTypes.func.isRequired, 82 | isDoctorAuthenticated: PropTypes.bool 83 | }; 84 | const mapStateToProps = state => ({ 85 | isDoctorAuthenticated: state.authDoctor.isDoctorAuthenticated 86 | }); 87 | 88 | export default connect(mapStateToProps, {login})(LoginDoctor); 89 | -------------------------------------------------------------------------------- /client/src/components/auth/LoginUser.js: -------------------------------------------------------------------------------- 1 | import React, { useState, Fragment } from 'react'; 2 | import {Link, Redirect} from 'react-router-dom'; 3 | import {connect} from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import {login} from '../../actions/authUser'; 6 | 7 | const LoginUser = ({ login, isUserAuthenticated }) => { 8 | const [formData, setFormData] = useState({ 9 | email: '', 10 | password: '' 11 | }); 12 | 13 | const {email, password} = formData; 14 | const onChange = e => setFormData({ 15 | ...formData, 16 | [e.target.name]: e.target.value 17 | }); 18 | const onSubmit = async e => { 19 | e.preventDefault(); 20 | login(email,password); 21 | } 22 | 23 | // Redirect if login 24 | if(isUserAuthenticated) { 25 | return 26 | } 27 | 28 | return ( 29 | 30 |
31 |
32 |
33 |
34 |
35 |

Log in User{' '} 36 | 37 |

38 |
39 |
onSubmit(e)}> 40 |
41 | 42 | onChange(e)} 49 | required 50 | /> 51 | We'll never share your email with anyone else. 52 |
53 |
54 | 55 | onChange(e)} 63 | /> 64 |
65 | 66 |

Or {' '} 67 | Create a new account

68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 |
76 |
77 | ); 78 | }; 79 | 80 | LoginUser.propTypes ={ 81 | login: PropTypes.func.isRequired, 82 | isUserAuthenticated: PropTypes.bool 83 | }; 84 | const mapStateToProps = state => ({ 85 | isUserAuthenticated: state.authUser.isUserAuthenticated 86 | }); 87 | 88 | export default connect(mapStateToProps, {login})(LoginUser); 89 | -------------------------------------------------------------------------------- /client/src/components/auth/UserRegister.js: -------------------------------------------------------------------------------- 1 | import React, { useState, Fragment } from 'react'; 2 | import {Redirect} from 'react-router-dom'; 3 | import {setAlert} from '../../actions/alert'; 4 | import { register} from '../../actions/authUser'; 5 | import PropTypes from 'prop-types'; 6 | import {connect} from 'react-redux'; 7 | 8 | 9 | const UserRegister = ({ setAlert, register, isUserAuthenticated }) => { 10 | const [formData, setFormData] = useState({ 11 | name: '', 12 | email: '', 13 | password: '', 14 | password2: '' 15 | }); 16 | 17 | const {name, email, password, password2} = formData; 18 | const onChange = e => setFormData({ 19 | ...formData, 20 | [e.target.name]: e.target.value 21 | }); 22 | const onSubmit = async e => { 23 | e.preventDefault(); 24 | if(password !== password2) { 25 | setAlert('Password do not match', 'danger'); 26 | } else { 27 | register({ name, email, password }); 28 | } 29 | } 30 | if(isUserAuthenticated) { 31 | return 32 | } 33 | 34 | return ( 35 | 36 |
37 |
38 |
39 |
40 |
41 |

User 42 | 43 |

44 |
45 |
onSubmit(e)}> 46 |
47 | 48 | onChange(e)} 55 | /> 56 | This site uses Gravatar so if you want a profile image, use a Gravatar email 57 |
58 |
59 | 60 | onChange(e)} 67 | /> 68 |
69 |
70 | 71 | onChange(e)} 78 | /> 79 |
80 |
81 | 82 | onChange(e)} 89 | /> 90 |
91 | 92 |
93 |
94 |
95 | 96 |
97 |
98 |
99 |
100 |
101 | ); 102 | }; 103 | 104 | UserRegister.propTypes = { 105 | setAlert: PropTypes.func.isRequired, 106 | register: PropTypes.func.isRequired, 107 | isUserAuthenticated: PropTypes.bool.isRequired 108 | } 109 | 110 | const mapStateToProps =state => ({ 111 | isUserAuthenticated: state.authUser.isUserAuthenticated 112 | }); 113 | 114 | export default connect(mapStateToProps, {setAlert, register})(UserRegister); 115 | -------------------------------------------------------------------------------- /client/src/components/bookappointment/AppointmentForm.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import {getProfileById} from '../../actions/profile'; 5 | import Form from './Form'; 6 | import { withRouter } from 'react-router-dom'; 7 | 8 | const AppointmentForm = ({ 9 | getProfileById, 10 | profile: {profileById}, 11 | match, 12 | history 13 | }) => { 14 | useEffect(() => { 15 | getProfileById(match.params.id) 16 | },[getProfileById, match.params.id]); 17 | 18 | return ( 19 | 20 |
21 |
22 |
23 |
24 | { 25 | profileById !== null ? 26 | ( 27 |
28 | ) : ( 29 | "" 30 | ) 31 | } 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 | ); 41 | }; 42 | 43 | AppointmentForm.propTypes = { 44 | getProfileById: PropTypes.func.isRequired, 45 | profile: PropTypes.object.isRequired 46 | }; 47 | 48 | const mapStateToProps = state => ({ 49 | profile: state.profile, 50 | }); 51 | 52 | export default connect(mapStateToProps, {getProfileById})(withRouter(AppointmentForm)); 53 | -------------------------------------------------------------------------------- /client/src/components/bookappointment/Form.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import {addAppointment} from '../../actions/appointment'; 5 | import { Link } from 'react-router-dom'; 6 | 7 | 8 | const Form = ({profile, doctorId,history, addAppointment}) => { 9 | 10 | const [formData, setFormData] = useState({ 11 | patientname: '', 12 | fathername: '', 13 | age:'', 14 | status:'', 15 | date:'', 16 | description:'' 17 | }); 18 | 19 | const { 20 | patientname, 21 | fathername, 22 | age, 23 | status, 24 | date, 25 | description 26 | } = formData; 27 | 28 | const onChange = e => setFormData({ 29 | ...formData, 30 | [e.target.name]: e.target.value 31 | }); 32 | return ( 33 | 34 |
35 |
36 |

Book Appointment 37 |

38 |

39 | Provide your details correctly and book your appointment. 40 |

41 |
42 | 43 |

{profile.name}

44 |
45 |
46 | { 47 | e.preventDefault(); 48 | addAppointment(doctorId, formData, history); 49 | }}> 50 | * = required field 51 |
52 | onChange(e)} /> 59 |
60 |
61 | onChange(e)} /> 68 |
69 |
70 | onChange(e)} /> 77 |
78 |
79 | onChange(e)} /> 86 | Status like profession (eg. student, job etc) 87 |
88 |
Date
89 |
90 | onChange(e)} /> 96 |
97 |
98 | 105 | Tell us about the Health Problem. 106 |
107 | {' '} 108 | Go Back 109 | 110 |
111 |
112 | ); 113 | }; 114 | 115 | Form.propTypes = { 116 | addAppointment: PropTypes.func.isRequired 117 | } 118 | 119 | export default connect(null, {addAppointment})(Form); 120 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import Spinner from '../layout/Spinner'; 5 | import Experience from './Experience'; 6 | import Education from './Education'; 7 | import Patient from './Patient'; 8 | import Review from './Review'; 9 | import {getCurrentProfile, deleteAccount} from '../../actions/profile'; 10 | import {Link} from 'react-router-dom'; 11 | 12 | 13 | const Dashboard = ({ 14 | getCurrentProfile, 15 | deleteAccount, 16 | authDoctor: {doctor}, 17 | profile: {profile, loading} 18 | }) => { 19 | useEffect(() => { 20 | getCurrentProfile(); 21 | }, [getCurrentProfile]); 22 | 23 | return loading && profile == null ? ( 24 | 25 | ) : ( 26 | 27 |
28 |
29 |
30 |

Dashboard

31 |

Welcome {doctor && doctor.name}

32 |
33 |
34 | {profile !== null ? ( 35 | 36 | {profile.patients !== null && profile.patients.length > 0 ? 37 | ( 38 | 39 | ) : ( 40 |
No Appointments yet..
41 | ) 42 | } 43 | 44 | 45 | 46 | 51 |
52 | ) : ( 53 | 54 |

You have not any Profile add your Profile..

55 | 56 | Create Profile 57 | 58 |
59 | )} 60 |
61 |
62 |
63 |
64 | ); 65 | }; 66 | 67 | Dashboard.propTypes = { 68 | getCurrentProfile: PropTypes.func.isRequired, 69 | deleteAccount: PropTypes.func.isRequired, 70 | authDoctor: PropTypes.object.isRequired, 71 | profile: PropTypes.object.isRequired 72 | } 73 | const mapStateToProps = state => ({ 74 | authDoctor: state.authDoctor, 75 | profile: state.profile 76 | }); 77 | 78 | export default connect(mapStateToProps, {getCurrentProfile, deleteAccount})(Dashboard); 79 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Education.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | import {connect} from 'react-redux'; 5 | import {deleteEducation} from '../../actions/profile'; 6 | 7 | const Education = ({education, deleteEducation}) => { 8 | const educations = education.map(edu => ( 9 | 10 | {edu.school} 11 | {edu.degree} 12 | 13 | {edu.from} - {' '} 14 | {edu.to === null ? ( 15 | 'Now' 16 | ) : ( 17 | {edu.to} 18 | )} 19 | 20 | 21 | 26 | 27 | 28 | )); 29 | return ( 30 | 31 |
32 |

Education Credentials

33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {educations} 43 |
SchoolDegreeYears
44 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | 51 | Education.propTypes = { 52 | education: PropTypes.array.isRequired, 53 | deleteEducation: PropTypes.func.isRequired, 54 | } 55 | 56 | export default connect(null, {deleteEducation})(Education); 57 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Experience.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | import {connect} from 'react-redux'; 5 | import {deleteExperience} from '../../actions/profile'; 6 | 7 | const Experience = ({experience, deleteExperience}) => { 8 | const experiences = experience.map(exp => ( 9 | 10 | {exp.medical} 11 | {exp.position} 12 | 13 | {exp.from} - {' '} 14 | {exp.to === null ? ( 15 | 'Now' 16 | ) : ( 17 | {exp.to} 18 | )} 19 | 20 | 21 | 26 | 27 | 28 | )); 29 | return ( 30 | 31 |
32 |

Experience Credentials

33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {experiences} 43 |
HospitalPostionYears
44 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | 51 | Experience.propTypes = { 52 | experience: PropTypes.array.isRequired, 53 | deleteExperience: PropTypes.func.isRequired, 54 | } 55 | 56 | export default connect(null, {deleteExperience})(Experience); 57 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Graph.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useState, useEffect} from 'react'; 2 | import {Line, Bar} from 'react-chartjs-2'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const Graph = ({patient}) => { 6 | const [chartData, setChartData] = useState([]); 7 | 8 | const chart = () => { 9 | const patientData = []; 10 | const jan = [0]; 11 | const feb = [0]; 12 | const mar = [0]; 13 | const apr = [0]; 14 | const may = [0]; 15 | const jun = [0]; 16 | const jul = [0]; 17 | const aug = [0]; 18 | const sep = [0]; 19 | const oct = [0]; 20 | const nov = [0]; 21 | const dec = [0]; 22 | 23 | patient.forEach(pat => { 24 | if(new Date(pat.date).getMonth() === 0) { 25 | return jan.push(jan[0]+1); 26 | } else if(new Date(pat.date).getMonth() === 1) { 27 | return feb.push(feb[0]+1); 28 | } else if(new Date(pat.date).getMonth() === 2) { 29 | return mar.push(mar[0]+1); 30 | } else if(new Date(pat.date).getMonth() === 3) { 31 | return apr.push(apr[0]+1); 32 | } else if(new Date(pat.date).getMonth() === 4) { 33 | return may.push(may[0]+1); 34 | } else if(new Date(pat.date).getMonth() === 5) { 35 | return jun.push(jun[0]+1); 36 | } else if(new Date(pat.date).getMonth() === 6) { 37 | return jul.push(jul[0]+1); 38 | } else if(new Date(pat.date).getMonth() === 7) { 39 | return aug.push(aug[0]+1); 40 | } else if(new Date(pat.date).getMonth() === 8) { 41 | return sep.push(sep[0]+1); 42 | } else if(new Date(pat.date).getMonth() === 9) { 43 | return oct.push(oct[0]+1); 44 | } else if(new Date(pat.date).getMonth() === 10) { 45 | return nov.push(nov[0]+1); 46 | } else if(new Date(pat.date).getMonth() === 11) { 47 | return dec.push(dec[0]+1); 48 | } else { 49 | } 50 | }); 51 | 52 | var janData = jan.reduce( (a, b) => a+b, jan[0]); 53 | var febData = feb.reduce( (a, b) => a+b, feb[0]); 54 | var marData = mar.reduce( (a, b) => a+b, mar[0]); 55 | var aprData = apr.reduce( (a, b) => a+b, apr[0]); 56 | var mayData = may.reduce( (a, b) => a+b, may[0]); 57 | var junData = jun.reduce( (a, b) => a+b, jun[0]); 58 | var julData = jul.reduce( (a, b) => a+b, jul[0]); 59 | var augData = aug.reduce( (a, b) => a+b, aug[0]); 60 | var sepData = sep.reduce( (a, b) => a+b, sep[0]); 61 | var octData = oct.reduce( (a, b) => a+b, oct[0]); 62 | var novData = nov.reduce( (a, b) => a+b, nov[0]); 63 | var decData = dec.reduce( (a, b) => a+b, dec[0]); 64 | 65 | patientData.push(janData); 66 | patientData.push(febData); 67 | patientData.push(marData); 68 | patientData.push(aprData); 69 | patientData.push(mayData); 70 | patientData.push(junData); 71 | patientData.push(julData); 72 | patientData.push(augData); 73 | patientData.push(sepData); 74 | patientData.push(octData); 75 | patientData.push(novData); 76 | patientData.push(decData); 77 | 78 | console.log(patientData); 79 | 80 | setChartData({ 81 | labels: [ 82 | "January", 83 | "February", 84 | "March", 85 | "April", 86 | "May", 87 | "June", 88 | "July", 89 | "August", 90 | "September", 91 | "October", 92 | "November", 93 | "December" 94 | ], 95 | datasets: [ 96 | { 97 | label: "level of patients", 98 | data: patientData, 99 | borderColor: [ 100 | "#17a2b8" 101 | ], 102 | pointBorderColor: [ 103 | "#17a2b8", 104 | "#17a2b8", 105 | "#17a2b8", 106 | "#17a2b8", 107 | "#17a2b8", 108 | "#17a2b8", 109 | "#17a2b8", 110 | "#17a2b8", 111 | "#17a2b8", 112 | "#17a2b8", 113 | "#17a2b8", 114 | "#17a2b8", 115 | ], 116 | borderWidth: 3 117 | } 118 | ] 119 | }); 120 | }; 121 | useEffect(() => { 122 | chart(); 123 | }, []); 124 | 125 | return ( 126 | 127 |
128 | 159 |
160 |
161 | ); 162 | }; 163 | 164 | Graph.propTypes = { 165 | patient: PropTypes.array.isRequired, 166 | } 167 | 168 | export default Graph; 169 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Patient.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment,useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | import {connect} from 'react-redux'; 5 | 6 | const Patient = ({patient}) => { 7 | 8 | const [value,setValue] = useState([]); 9 | 10 | const modalBody = patient.map(pat => ( 11 | 12 |

{pat.patientname}

13 |

Father's name: {pat.fathername}

14 |

Age: {pat.age}

15 |

Status: {pat.status}

16 |

Date: {pat.date}

17 |

Booking ID: {pat.bookingId}

18 |
19 | )); 20 | const patients = patient.map(ptn => ( 21 | 22 | {ptn.bookingId} 23 | {ptn.patientname} 24 | 25 | {ptn.date} 26 | 27 | 28 | 35 | 67 | 68 | 69 | )); 70 | 71 | return ( 72 | 73 |
74 |

Patient Credentials

75 |
76 |
77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {patients} 89 | 90 |
Booking IDPatient's NameDate
91 |
92 |
93 |
94 |
95 |
96 | ); 97 | }; 98 | 99 | Patient.propTypes = { 100 | patient: PropTypes.array.isRequired, 101 | } 102 | 103 | export default connect(null)(Patient); 104 | -------------------------------------------------------------------------------- /client/src/components/dashboard/Review.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState, useEffect } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | import {connect} from 'react-redux'; 5 | import Graph from './Graph'; 6 | 7 | const Review = ({review, patient}) => { 8 | const reviews = review.map(rev => ( 9 |
10 | Card image cap 11 |
12 |

{rev.text}

13 | 16 | {rev.name} 17 |
18 |
19 | )); 20 | return ( 21 | 22 |
23 |
24 |

Users Reviews

25 |
26 |
27 |
28 | { review.length === 0 ? "No Reviews Yet" : reviews } 29 |
30 |
31 |
32 |
33 |

Total Appointments

34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | Review.propTypes = { 46 | patient: PropTypes.array.isRequired, 47 | } 48 | 49 | export default connect(null)(Review); 50 | -------------------------------------------------------------------------------- /client/src/components/layout/Alert.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { connect } from 'react-redux'; 4 | 5 | const Alert = ({ alerts }) => alerts !== null && alerts.length > 0 && alerts.map(alert => ( 6 |
7 | {alert.msg} 8 |
9 | )); 10 | 11 | Alert.propTypes = { 12 | alerts: PropTypes.array.isRequired 13 | } 14 | 15 | const mapStateToProps = state => ({ 16 | alerts: state.alert 17 | }); 18 | 19 | export default connect(mapStateToProps)(Alert); 20 | -------------------------------------------------------------------------------- /client/src/components/layout/Landing.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import { Link, Redirect } from 'react-router-dom'; 3 | import {connect} from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | 6 | import '../../App.css'; 7 | 8 | const Landing = ({isDoctorAuthenticated, isUserAuthenticated}) => { 9 | if(isDoctorAuthenticated){ 10 | return 11 | } else if(isUserAuthenticated) { 12 | return 13 | } 14 | 15 | return ( 16 | 17 |
18 |
19 |
20 |

Find Your Best Doctor &

21 |

Book Your Appointment.

22 |
23 |
24 |
25 |

For Doctors

26 |

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Vel itaque quae delectus veritatis consequatur hic!

27 | Sign Up 28 |
29 |
30 |

For Users

31 |

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Vel itaque quae delectus veritatis consequatur hic!

32 | Sign Up 33 |
34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 | ); 46 | }; 47 | Landing.propTypes = { 48 | isDoctorAuthenticated: PropTypes.bool.isRequired, 49 | isUserAuthenticated: PropTypes.bool.isRequired 50 | } 51 | 52 | const mapStateToProps = state => ({ 53 | isDoctorAuthenticated: state.authDoctor.isDoctorAuthenticated, 54 | isUserAuthenticated: state.authUser.isUserAuthenticated 55 | }); 56 | 57 | export default connect(mapStateToProps)(Landing); 58 | -------------------------------------------------------------------------------- /client/src/components/layout/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import {Link} from 'react-router-dom'; 3 | import {connect} from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import { logout_user } from '../../actions/authUser'; 6 | import { logout_doctor } from '../../actions/authDoctor'; 7 | 8 | import '../../App.css'; 9 | 10 | const Navbar = ( 11 | { 12 | authUser: { isUserAuthenticated, loadingUser, user}, logout_user, 13 | authDoctor: {isDoctorAuthenticated, loadingDoctor, doctor}, logout_doctor 14 | } 15 | ) => { 16 | 17 | const authUserLinks = ( 18 | 19 | 20 | Cure 21 | 22 | 26 | 43 | 44 | ); 45 | const authDoctorLinks = ( 46 | 47 | 48 | Cure 49 | 50 | 54 | 80 | 81 | ) 82 | const guestLinks = ( 83 | 84 | 85 | Cure 86 | 87 | 91 | 116 | 117 | ) 118 | 119 | return ( 120 | 139 | ); 140 | }; 141 | Navbar.propTypes = { 142 | logout_user: PropTypes.func.isRequired, 143 | logout_doctor: PropTypes.func.isRequired, 144 | authUser: PropTypes.object.isRequired, 145 | authDoctor: PropTypes.object.isRequired 146 | }; 147 | 148 | const mapStateToProps = state => ({ 149 | authUser: state.authUser, 150 | authDoctor: state.authDoctor 151 | }); 152 | 153 | export default connect(mapStateToProps, {logout_user, logout_doctor})(Navbar); 154 | -------------------------------------------------------------------------------- /client/src/components/layout/Spinner.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | 3 | export default () => ( 4 | 5 |
6 |
13 | Loading... 14 |
15 |
16 |
17 | ); -------------------------------------------------------------------------------- /client/src/components/layout/doctor (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeevantheDev/Doctor-Assigned/87f6c1cb7e814e0c5c628d1e5393a227c9719487/client/src/components/layout/doctor (1).png -------------------------------------------------------------------------------- /client/src/components/profile-forms/AddEducation.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useState} from 'react'; 2 | import {Link, withRouter} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | import {addEducation} from '../../actions/profile'; 6 | 7 | 8 | const AddEducation = ({addEducation, history}) => { 9 | const [formData, setFormdata] = useState({ 10 | school: '', 11 | degree: '', 12 | fieldofstudy: '', 13 | from:'', 14 | to:'', 15 | current: false, 16 | description: '' 17 | }); 18 | 19 | const [toDateDisabled, toggleDisabled] = useState(false); 20 | 21 | const {school, degree, fieldofstudy, from, to, current, description} = formData; 22 | 23 | const onChange = e => setFormdata({ 24 | ...formData, 25 | [e.target.name]: e.target.value 26 | }); 27 | 28 | return ( 29 | 30 |
31 |
32 |
33 |
34 |
35 |

Add Education 36 | 37 |

38 |

39 | Add any school, Medicalcamp, etc that you have attended 40 |

41 |
42 |
{ 43 | e.preventDefault(); 44 | addEducation(formData, history); 45 | }}> 46 | * = required field 47 |
48 | onChange(e)} 55 | required 56 | /> 57 |
58 |
59 | onChange(e)} 66 | required 67 | /> 68 |
69 |
70 | onChange(e)} 77 | required 78 | /> 79 |
80 | 81 |
From Date
82 |
83 | onChange(e)} /> 84 |
85 |
86 |

{ 87 | setFormdata({ ...formData, current: !current }); 88 | toggleDisabled(!toDateDisabled); 89 | }} /> {' '} Current School

90 |
91 |
To Date
92 |
93 | onChange(e)} disabled={toDateDisabled ? 'disabled' : ''} /> 99 |
100 |
101 | 106 | Tell us a little about the program. 107 |
108 | {' '} 109 | Go Back 110 |
111 |
112 |
113 |
114 | 115 |
116 |
117 |
118 |
119 |
120 | ); 121 | }; 122 | 123 | AddEducation.propTypes = { 124 | addEducation: PropTypes.func.isRequired 125 | } 126 | 127 | export default connect(null, {addEducation})(withRouter(AddEducation)); 128 | -------------------------------------------------------------------------------- /client/src/components/profile-forms/AddExperience.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useState} from 'react'; 2 | import {Link, withRouter} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | import {addExperience} from '../../actions/profile'; 6 | 7 | const AddExperience = ({addExperience, history}) => { 8 | const [formData, setFormData] = useState({ 9 | medical: '', 10 | position: '', 11 | location: '', 12 | from: '', 13 | to: '', 14 | current: false, 15 | description: '' 16 | }); 17 | 18 | const [toDateDisabled, toggleDisabled] = useState(false); 19 | 20 | const {medical, position, location, from, to, current, description} = formData; 21 | 22 | const onChange = e => setFormData({ 23 | ...formData, 24 | [e.target.name]: e.target.value 25 | }); 26 | return ( 27 |
28 | 29 |
30 |
31 |
32 |
33 |
34 |

Add Experience 35 | 36 |

37 |

38 | Add any job or position that you have had in the past or current 39 |

40 |
41 |
{ 42 | e.preventDefault(); 43 | addExperience(formData, history); 44 | }}> 45 | * = required field 46 |
47 | onChange(e)} 54 | required 55 | /> 56 |
57 |
58 | onChange(e)} 65 | required 66 | /> 67 |
68 |
69 | onChange(e)} 76 | required 77 | /> 78 |
79 | 80 |
From Date
81 |
82 | onChange(e)} /> 83 |
84 |
85 |

{ 86 | setFormData({ ...formData, current: !current }); 87 | toggleDisabled(!toDateDisabled); 88 | }} /> {' '} Current Job

89 |
90 |
To Date
91 |
92 | onChange(e)} disabled={toDateDisabled ? 'disabled' : ''} /> 98 |
99 |
100 | 105 | Tell us a little about the job. 106 |
107 | {' '} 108 | Go Back 109 |
110 |
111 |
112 |
113 | 114 |
115 |
116 |
117 |
118 |
119 |
120 | ) 121 | }; 122 | AddExperience.propTypes = { 123 | addExperience: PropTypes.func.isRequired 124 | } 125 | 126 | export default connect(null, {addExperience})(withRouter(AddExperience)); 127 | -------------------------------------------------------------------------------- /client/src/components/profile/Profile.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useEffect} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import Spinner from '../layout/Spinner'; 5 | import {getProfileById} from '../../actions/profile'; 6 | import ProfileTop from './ProfileTop'; 7 | import ProfileAbout from './ProfileAbout'; 8 | import ProfileExperience from './ProfileExperience'; 9 | import ProfileEducation from './ProfileEducation'; 10 | import ProfileReview from './ProfileReview'; 11 | import ReviewForm from '../profile/ReviewForm'; 12 | import { Link } from 'react-router-dom'; 13 | 14 | const Profile = ({ 15 | getProfileById, 16 | profile: {profileById, loading}, authDoctor,authUser, match 17 | }) => { 18 | useEffect(() => { 19 | getProfileById(match.params.id); 20 | },[getProfileById, match.params.id]); 21 | 22 | return ( 23 | 24 | {profileById === null || loading ? ( 25 | 26 | ) : ( 27 | 28 |
29 |
30 |
31 |
32 | Back to Profiles 33 | {authUser.isUserAuthenticated ? ( 34 | 35 | Book Appointment 36 | 37 | ) : ( 38 | 39 | 40 | Book Appointment 41 | 42 | 43 | ) 44 | } 45 | {authDoctor.isDoctorAuthenticated && 46 | authDoctor.loadingDoctor === false && 47 | authDoctor.doctor._id === profileById.doctor._id && ( 48 | 49 | 50 | 51 | )} 52 |
53 | 54 | 55 |
56 |
57 |

Experience

58 | {profileById.experience.length > 0 ? ( 59 | 60 | {profileById.experience.map(experience => ( 61 | 62 | ))} 63 | 64 | ) : ( 65 |

No Experience credentials

66 | ) 67 | } 68 |
69 |
70 |

Education

71 | {profileById.education.length > 0 ? ( 72 | 73 | {profileById.education.map(education => ( 74 | 75 | ))} 76 | 77 | ) : ( 78 |

No education credentials

79 | ) 80 | } 81 |
82 |
83 |
84 |

Patient Reviews

85 | { authUser.isUserAuthenticated ? 86 | ( 87 | 88 | ) : "" 89 | } 90 | { 91 | profileById.review.map(rev => ( 92 | 93 | )) 94 | } 95 |
96 |
97 |
98 |
99 |
100 | )} 101 |
102 | ); 103 | }; 104 | 105 | Profile.propTypes = { 106 | getProfileById: PropTypes.func.isRequired, 107 | profile: PropTypes.object.isRequired, 108 | authDoctor: PropTypes.object.isRequired, 109 | authUser: PropTypes.object.isRequired 110 | }; 111 | 112 | const mapStateToProps = state => ({ 113 | profile: state.profile, 114 | authDoctor: state.authDoctor, 115 | authUser: state.authUser 116 | }); 117 | 118 | export default connect(mapStateToProps, {getProfileById})(Profile); 119 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileAbout.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ProfileAbout = ({ 5 | profile: { 6 | bio, 7 | timing, 8 | doctor: {name} 9 | } 10 | }) => { 11 | return ( 12 | 13 |
14 |
15 |

Dr. {(name.split(' ')[1])}'s Bio

16 |

{bio}

17 |
18 |
19 |
20 |

Timing

21 |

{timing}

22 |
23 |
24 |
25 |
26 | ) 27 | }; 28 | 29 | ProfileAbout.propTypes = { 30 | profile: PropTypes.object.isRequired 31 | }; 32 | 33 | export default ProfileAbout; 34 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileEducation.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | 5 | const ProfileEducation = ({ 6 | education : { 7 | school, degree, fieldofstudy,from, to, description 8 | } 9 | }) => { 10 | return ( 11 | 12 |
13 |
{school}
14 |

{from} - 15 | { 16 | !to ? 'Now' : {to} 17 | } 18 |

19 |

Degree: {degree}

20 |

Field of Study: {fieldofstudy}

21 |

Description: {description}

22 |
23 |
24 |
25 | ) 26 | }; 27 | 28 | ProfileEducation.propTypes = { 29 | education: PropTypes.array.isRequired 30 | }; 31 | 32 | export default ProfileEducation; 33 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileExperience.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | 5 | const ProfileExperience = ({ 6 | experience: { 7 | medical, position, location ,from, to, description 8 | } 9 | }) => { 10 | return ( 11 | 12 |
13 |
{medical}
14 |

15 | {from} - { 16 | !to ? 'Now' : {to} 17 | } 18 |

19 |

Position: {position}

20 |

Location: {location}

21 |

Description: {description}

22 |
23 |
24 |
25 | ) 26 | }; 27 | 28 | ProfileExperience.propTypes = { 29 | experience: PropTypes.array.isRequired 30 | }; 31 | 32 | export default ProfileExperience; 33 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileReview.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Moment from 'react-moment'; 4 | import {deleteReview} from '../../actions/profile'; 5 | import { connect } from 'react-redux'; 6 | 7 | const ProfileReview = ({ 8 | doctorId, 9 | review: { _id, text, name, avatar, date, user}, 10 | authUser, 11 | deleteReview 12 | }) => { 13 | 14 | return ( 15 | 16 |
17 |
18 | 19 |
{name}
20 |
21 |
22 |

{text}

23 |

24 | Posted on {date} 25 |

26 | { 27 | authUser.user !== null && user === authUser.user._id && ( 28 | 31 | )} 32 |
33 |
34 |
35 | ) 36 | }; 37 | 38 | ProfileReview.propTypes = { 39 | profileId: PropTypes.number.isRequired, 40 | review: PropTypes.object.isRequired, 41 | authUser: PropTypes.object.isRequired, 42 | deleteReview: PropTypes.func.isRequired 43 | }; 44 | const mapStateToProps = state => ({ 45 | authUser: state.authUser 46 | }); 47 | 48 | export default connect(mapStateToProps, {deleteReview})(ProfileReview); 49 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileReviewForm.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import {addReview} from '../../actions/profile'; 5 | 6 | const ProfileReviewForm = () => { 7 | return ( 8 |
9 | 10 |
11 | ) 12 | } 13 | 14 | export default ProfileReviewForm 15 | -------------------------------------------------------------------------------- /client/src/components/profile/ProfileTop.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ProfileTop = ({profile: { 5 | doctor: { name, avatar }, 6 | clinic, 7 | location, 8 | specialists, 9 | ruppess, 10 | website, 11 | social 12 | } 13 | }) => { 14 | return ( 15 |
16 |
17 |
18 | 19 |
20 |
21 |

{name}

22 |
23 |

{specialists}

24 |
25 |

{clinic}, {location}

26 |

{ruppess} Consultation Fee

27 |
28 | {website && ( 29 | 30 | 31 | 32 | )} 33 | { social && social.twitter && ( 34 | 35 | 36 | 37 | )} 38 | { social && social.facebook && ( 39 | 40 | 41 | 42 | )} 43 | { social && social.instagram && ( 44 | 45 | 46 | 47 | )} 48 | { social && social.youtube && ( 49 | 50 | 51 | 52 | )} 53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 | ) 61 | }; 62 | 63 | ProfileTop.propTypes = { 64 | profile: PropTypes.object.isRequired 65 | }; 66 | 67 | export default ProfileTop; 68 | -------------------------------------------------------------------------------- /client/src/components/profile/ReviewForm.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import {addReview} from '../../actions/review'; 5 | 6 | const ReviewForm = ({ doctorId, addReview }) => { 7 | const [text, setText] = useState(''); 8 | 9 | return ( 10 | 11 |
12 |
{ 13 | e.preventDefault(); 14 | addReview(doctorId, { text }); 15 | setText(''); 16 | }}> 17 | 26 | 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | ReviewForm.propTypes = { 34 | addReview: PropTypes.func.isRequired 35 | }; 36 | 37 | export default connect(null, {addReview})(ReviewForm); 38 | -------------------------------------------------------------------------------- /client/src/components/profiles/ProfileItem.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState } from 'react'; 2 | import {Link} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | 6 | 7 | const ProfileItem = ({ 8 | profile: { 9 | doctor: {_id, name, avatar }, 10 | clinic, 11 | location, 12 | specialists, 13 | ruppess 14 | }, 15 | authUser 16 | }) => { 17 | 18 | return ( 19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |

{name}

27 |

{specialists}

28 |

{clinic} {location}

29 |

{ruppess} Consultation fee at clinic

30 |
31 |
32 |
33 | {authUser.isUserAuthenticated ? ( 34 | 35 | Book Appointment 36 | 37 | ) : ( 38 | 39 | 42 | 43 | ) 44 | } 45 | View Profile 46 |
47 |
48 |
49 | ) 50 | }; 51 | 52 | ProfileItem.propTypes ={ 53 | profile: PropTypes.object.isRequired, 54 | authUser: PropTypes.object.isRequired 55 | }; 56 | const mapStateToProps =state => ({ 57 | authUser: state.authUser 58 | }); 59 | 60 | export default connect(mapStateToProps)(ProfileItem); 61 | -------------------------------------------------------------------------------- /client/src/components/profiles/Profiles.js: -------------------------------------------------------------------------------- 1 | import React, {Fragment, useEffect} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import Spinner from '../layout/Spinner'; 5 | import {Dots} from 'react-preloaders'; 6 | import ProfileItem from './ProfileItem'; 7 | import {getProfiles} from '../../actions/profile'; 8 | 9 | const Profiles = ({getProfiles, profile: { profiles,loading }}) => { 10 | useEffect(() => { 11 | getProfiles(); 12 | },[getProfiles]); 13 | 14 | return ( 15 | 16 | { loading ? : 17 | 18 |
19 |
20 |
21 |

Doctor Profiles

22 |
23 |

Book your Appointments

24 |
25 | { 26 | profiles != null ? ( 27 | profiles.map(profile => ( 28 | 29 | )) 30 | ) :

No Profiles found..

31 | } 32 |
33 |
34 |
35 | } 36 |
37 | ) 38 | }; 39 | 40 | Profiles.propTypes = { 41 | getProfiles: PropTypes.func.isRequired, 42 | profile: PropTypes.object.isRequired 43 | }; 44 | 45 | const mapStateToProps = state => ({ 46 | profile: state.profile 47 | }); 48 | 49 | export default connect(mapStateToProps, {getProfiles})(Profiles); 50 | -------------------------------------------------------------------------------- /client/src/components/routing/PrivateDoctorRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route, Redirect} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | 6 | 7 | const PrivateDoctorRoute = ({component: Component, 8 | authDoctor: {isDoctorAuthenticated, loadingDoctor}, 9 | ...rest}) => ( 10 | 13 | !isDoctorAuthenticated && !loadingDoctor ? ( 14 | 15 | ) : ( 16 | 17 | ) 18 | } 19 | /> 20 | ); 21 | 22 | PrivateDoctorRoute.propTypes = { 23 | authDoctor: PropTypes.object.isRequired 24 | } 25 | const mapStateToProps = state => ({ 26 | authDoctor: state.authDoctor 27 | }); 28 | 29 | export default connect(mapStateToProps)(PrivateDoctorRoute); 30 | -------------------------------------------------------------------------------- /client/src/components/routing/PrivateUserRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Route, Redirect} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | 6 | 7 | const PrivateUserRoute = ({component: Component, 8 | authUser: {isUserAuthenticated, loadingUser}, 9 | ...rest}) => ( 10 | 13 | !isUserAuthenticated && !loadingUser ? ( 14 | 15 | ) : ( 16 | 17 | ) 18 | } 19 | /> 20 | ); 21 | 22 | PrivateUserRoute.propTypes = { 23 | authUser: PropTypes.object.isRequired 24 | } 25 | const mapStateToProps = state => ({ 26 | authUser: state.authUser 27 | }); 28 | 29 | export default connect(mapStateToProps)(PrivateUserRoute); 30 | -------------------------------------------------------------------------------- /client/src/components/user/AppointmentItems.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import {Link} from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | import {connect} from 'react-redux'; 5 | import Moment from 'react-moment'; 6 | import {deleteAppointment} from '../../actions/appointment'; 7 | 8 | const AppointmentItems = ({ 9 | appointment, deleteAppointment, 10 | }) => { 11 | 12 | const appointments = appointment.map(appnt => ( 13 |
14 |
15 | 16 | 17 | 18 |
{appnt.name}
19 |
20 |
21 |
22 |

{appnt.patientname}

23 |

Father's name: {appnt.fathername}

24 |

Age: {appnt.age}

25 |

Status: {appnt.status}

26 |

Date: {appnt.date}

27 |

Booking ID: {appnt.bookingId}

28 |
29 |
30 |
31 |

Description: {appnt.description}

32 | 33 |
34 |
35 | )); 36 | return ( 37 | 38 | {appointments} 39 | 40 | ); 41 | }; 42 | 43 | AppointmentItems.propTypes = { 44 | appointment: PropTypes.array.isRequired, 45 | deleteAppointment: PropTypes.func.isRequired 46 | } 47 | 48 | export default connect(null, {deleteAppointment})(AppointmentItems); 49 | -------------------------------------------------------------------------------- /client/src/components/user/Appointments.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, Fragment } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {connect} from 'react-redux'; 4 | import Spinner from '../layout/Spinner'; 5 | import {Dots} from 'react-preloaders'; 6 | import AppointmentItems from './AppointmentItems'; 7 | import {getAppointments, deleteAccountUser} from '../../actions/appointment'; 8 | 9 | const Appointments = ({ 10 | getAppointments, 11 | deleteAccountUser, 12 | authUser: {user}, 13 | appointment: {appointments, loading} 14 | }) => { 15 | useEffect(() => { 16 | getAppointments(); 17 | }, [getAppointments]) 18 | 19 | return ( 20 | 21 | { loading && appointments !== null ? : 22 | 23 |
24 |
25 |
26 |

Appointments

27 |

{' '} 28 | {user && (user.name.split(' ')[0].toLocaleUpperCase())}'s appointments

29 |
30 | 34 |
35 |
36 |
37 |
38 | {appointments !== null && appointments.appointments.length !== 0 ? ( 39 |
40 | 41 | 42 | 43 |
44 | ) : ( 45 |

No Appointments found...

46 | ) 47 | } 48 |
49 |
50 |
51 |
52 | } 53 |
54 | ) 55 | } 56 | 57 | Appointments.propTypes = { 58 | getAppointments: PropTypes.func.isRequired, 59 | deleteAccountUser: PropTypes.func.isRequired, 60 | authUser: PropTypes.object.isRequired, 61 | appointment: PropTypes.object.isRequired 62 | } 63 | 64 | const mapStateToProps = state => ({ 65 | authUser: state.authUser, 66 | appointment: state.appointment 67 | }); 68 | 69 | export default connect(mapStateToProps, {getAppointments, deleteAccountUser})(Appointments); 70 | -------------------------------------------------------------------------------- /client/src/img/doctor8.svg: -------------------------------------------------------------------------------- 1 | 61-70 -------------------------------------------------------------------------------- /client/src/img/undraw_account_490v.svg: -------------------------------------------------------------------------------- 1 | account -------------------------------------------------------------------------------- /client/src/img/undraw_forms_78yw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/img/undraw_medical_research_qg4d.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /client/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /client/src/reducers/alert.js: -------------------------------------------------------------------------------- 1 | import { SET_ALERT, REMOVE_ALERT } from '../actions/types'; 2 | 3 | const initialState = []; 4 | 5 | export default function( state = initialState, action) { 6 | const {type, payload} = action; 7 | switch (type) { 8 | case SET_ALERT: 9 | return [...state, payload]; 10 | case REMOVE_ALERT: 11 | return state.filter(alert => alert.id !== payload); 12 | default: 13 | return state; 14 | } 15 | } -------------------------------------------------------------------------------- /client/src/reducers/appointment.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_APPOINTMENTS, 3 | ADD_APPOINTMENTS, 4 | UPDATE_APPOINTMENTS, 5 | APPOINTMENT_ERROR, 6 | } from '../actions/types'; 7 | 8 | const initalState = { 9 | appointments: null, 10 | loading: true, 11 | error: {} 12 | } 13 | 14 | export default function(state=initalState, action) { 15 | const {type, payload} = action; 16 | 17 | switch(type) { 18 | case GET_APPOINTMENTS: 19 | return { 20 | ...state, 21 | appointments: payload, 22 | loading: false 23 | }; 24 | case ADD_APPOINTMENTS: 25 | return { 26 | ...state, 27 | appointments: payload, 28 | loading: false 29 | }; 30 | case UPDATE_APPOINTMENTS: 31 | return { 32 | ...state, 33 | appointments: payload, 34 | loading: false 35 | } 36 | case APPOINTMENT_ERROR: 37 | return { 38 | ...state, 39 | error: payload, 40 | loading: false 41 | }; 42 | default: 43 | return state; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /client/src/reducers/authDoctor.js: -------------------------------------------------------------------------------- 1 | import { 2 | REGISTER_DOCTOR_SUCCESS, 3 | REGISTER_DOCTOR_FAIL, 4 | DOCTOR_LOADED, 5 | AUTH_DOCTOR_ERROR, 6 | LOGIN_DOCTOR_SUCCESS, 7 | LOGIN_DOCTOR_FAIL, 8 | LOGOUT_DOCTOR, 9 | DELETE_ACCOUNT 10 | 11 | } from '../actions/types'; 12 | 13 | const initialState = { 14 | token: localStorage.getItem('token'), 15 | isDoctorAuthenticated: null, 16 | loadingDoctor: true, 17 | doctor: null 18 | } 19 | 20 | export default function(state = initialState, action) { 21 | const {type, payload} = action; 22 | switch (type) { 23 | case DOCTOR_LOADED: 24 | return { 25 | ...state, 26 | isDoctorAuthenticated: true, 27 | loadingDoctor: false, 28 | doctor: payload 29 | }; 30 | case REGISTER_DOCTOR_SUCCESS: 31 | case LOGIN_DOCTOR_SUCCESS: 32 | localStorage.setItem('token', payload.token); 33 | return { 34 | ...state, 35 | ...payload, 36 | isDoctorAuthenticated: true, 37 | loadingDoctor: false 38 | }; 39 | case REGISTER_DOCTOR_FAIL: 40 | case LOGIN_DOCTOR_FAIL: 41 | case DELETE_ACCOUNT: 42 | localStorage.removeItem('token'); 43 | return { 44 | ...state, 45 | token: null, 46 | isDoctorAuthenticated: false, 47 | loadingDoctor: false 48 | }; 49 | case LOGOUT_DOCTOR: 50 | case AUTH_DOCTOR_ERROR: 51 | localStorage.removeItem('token'); 52 | return { 53 | ...state, 54 | token: null, 55 | isDoctorAuthenticated: false, 56 | loadingDoctor: false 57 | }; 58 | default: 59 | return state; 60 | } 61 | } -------------------------------------------------------------------------------- /client/src/reducers/authUser.js: -------------------------------------------------------------------------------- 1 | import { 2 | REGISTER_USER_SUCCESS, 3 | REGISTER_USER_FAIL, 4 | USER_LOADED, 5 | AUTH_USER_ERROR, 6 | LOGIN_USER_SUCCESS, 7 | LOGIN_USER_FAIL, 8 | LOGOUT_USER, 9 | DELETE_USER_ACCOUNT 10 | } from '../actions/types'; 11 | 12 | const initialState = { 13 | token: localStorage.getItem('token'), 14 | isUserAuthenticated: null, 15 | loadingUser: true, 16 | user: null 17 | } 18 | 19 | export default function(state = initialState, action) { 20 | const {type, payload} = action; 21 | switch (type) { 22 | case USER_LOADED: 23 | return { 24 | ...state, 25 | isUserAuthenticated: true, 26 | loadingUser: false, 27 | user: payload 28 | }; 29 | case REGISTER_USER_SUCCESS: 30 | case LOGIN_USER_SUCCESS: 31 | localStorage.setItem('token', payload.token); 32 | return { 33 | ...state, 34 | ...payload, 35 | isUserAuthenticated: true, 36 | loadingUser: false 37 | }; 38 | case REGISTER_USER_FAIL: 39 | case LOGIN_USER_FAIL: 40 | localStorage.removeItem('token'); 41 | return { 42 | ...state, 43 | token: null, 44 | isUserAuthenticated: false, 45 | loadingUser: false 46 | }; 47 | case AUTH_USER_ERROR: 48 | case LOGOUT_USER: 49 | case DELETE_USER_ACCOUNT: 50 | localStorage.removeItem('token'); 51 | return { 52 | ...state, 53 | token: null, 54 | isUserAuthenticated: false, 55 | loadingUser: false 56 | }; 57 | default: 58 | return state; 59 | } 60 | } -------------------------------------------------------------------------------- /client/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import alert from './alert'; 3 | import authDoctor from './authDoctor'; 4 | import authUser from './authUser'; 5 | import profile from './profile'; 6 | import appointment from './appointment'; 7 | 8 | export default combineReducers({ 9 | alert, 10 | authDoctor, 11 | authUser, 12 | profile, 13 | appointment 14 | }); 15 | -------------------------------------------------------------------------------- /client/src/reducers/profile.js: -------------------------------------------------------------------------------- 1 | import { 2 | GET_PROFILE, 3 | GET_PROFILE_BY_ID, 4 | GET_PROFILES, 5 | UPDATE_PROFILE, 6 | PROFILE_ERROR, 7 | CLEAR_PROFILE, 8 | ADD_REVIEW, 9 | ADD_REVIEW_ERROR, 10 | REMOVE_REVIEW 11 | } from '../actions/types'; 12 | 13 | const initialState = { 14 | profileById: null, 15 | profile: null, 16 | profiles: [], 17 | loading: true, 18 | error: {} 19 | } 20 | 21 | export default function( state = initialState, action) { 22 | const {type, payload} = action; 23 | 24 | switch (type) { 25 | case GET_PROFILE: 26 | case UPDATE_PROFILE: 27 | return { 28 | ...state, 29 | profile: payload, 30 | loading: false 31 | }; 32 | case GET_PROFILE_BY_ID: 33 | return { 34 | ...state, 35 | profileById: payload, 36 | loading: false 37 | }; 38 | 39 | case ADD_REVIEW: 40 | return { 41 | ...state, 42 | profileById: { ...state.profileById, review: payload }, 43 | loading: false 44 | }; 45 | case REMOVE_REVIEW: 46 | return { 47 | ...state, 48 | profileById: { 49 | ...state.profileById, 50 | review: state.profileById.review.filter(comment => comment._id !== payload) 51 | }, 52 | loading: false 53 | }; 54 | case GET_PROFILES: 55 | return { 56 | ...state, 57 | profiles: payload, 58 | loading: false 59 | }; 60 | case PROFILE_ERROR: 61 | case ADD_REVIEW_ERROR: 62 | return { 63 | ...state, 64 | error: payload, 65 | loading: false 66 | }; 67 | case CLEAR_PROFILE: 68 | return { 69 | ...state, 70 | profile: null, 71 | loading: false 72 | }; 73 | 74 | default: 75 | return state; 76 | } 77 | } -------------------------------------------------------------------------------- /client/src/reducers/review.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JeevantheDev/Doctor-Assigned/87f6c1cb7e814e0c5c628d1e5393a227c9719487/client/src/reducers/review.js -------------------------------------------------------------------------------- /client/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import { composeWithDevTools } from 'redux-devtools-extension'; 3 | import thunk from 'redux-thunk'; 4 | import rootReducer from './reducers'; 5 | 6 | const initialState = {}; 7 | 8 | const middleWare = [thunk]; 9 | 10 | const store = createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(...middleWare))); 11 | 12 | export default store; -------------------------------------------------------------------------------- /client/src/utils/setAuthToken.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setAuthToken = token => { 4 | if(token) { 5 | axios.defaults.headers.common['x-auth-token'] = token; 6 | } else { 7 | delete axios.defaults.headers.common['x-auth-token']; 8 | } 9 | } 10 | 11 | export default setAuthToken; -------------------------------------------------------------------------------- /config/keys.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongoURI: "mongodb://localhost:27017/doctorconnectorDB" 3 | }; -------------------------------------------------------------------------------- /middleware/authDoctor.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const jwt = require('jsonwebtoken'); 3 | 4 | module.exports = function(req, res, next) { 5 | // Get token from the header 6 | const token = req.header('x-auth-token'); 7 | 8 | // Check if no token 9 | if(!token) { 10 | return res.status(401).json({msg: "No token"}); 11 | } 12 | 13 | // Verify token 14 | try { 15 | const decoded = jwt.verify(token, process.env.jwtSecret); 16 | 17 | req.doctor = decoded.doctor; 18 | next(); 19 | } catch (err) { 20 | res.status(401).json({ msg: "token is not valid " }); 21 | } 22 | } -------------------------------------------------------------------------------- /middleware/authUser.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const jwt = require('jsonwebtoken'); 3 | 4 | module.exports = function(req, res, next) { 5 | // Get token from the header 6 | const token = req.header('x-auth-token'); 7 | 8 | // Check if no token 9 | if(!token) { 10 | return res.status(401).json({msg: "No token"}); 11 | } 12 | 13 | // Verify token 14 | try { 15 | const decoded = jwt.verify(token, process.env.jwtSecret); 16 | 17 | req.user = decoded.user; 18 | next(); 19 | } catch (err) { 20 | res.status(401).json({ msg: "token is not valid " }); 21 | } 22 | } -------------------------------------------------------------------------------- /models/Doctor.js: -------------------------------------------------------------------------------- 1 | const mongoose = require ('mongoose'); 2 | 3 | const DoctorSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true 7 | }, 8 | email: { 9 | type: String, 10 | required: true, 11 | unique: true 12 | }, 13 | password: { 14 | type: String, 15 | required: true 16 | }, 17 | avatar: { 18 | type: String 19 | }, 20 | date: { 21 | type: Date, 22 | default: Date.now 23 | } 24 | }); 25 | 26 | module.exports = Doctor = mongoose.model('doctor', DoctorSchema); -------------------------------------------------------------------------------- /models/Profile.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | const ProfileSchema = new mongoose.Schema({ 5 | doctor: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: 'doctor' 8 | }, 9 | clinic: { 10 | type: String 11 | }, 12 | website: { 13 | type: String 14 | }, 15 | location: { 16 | type: String 17 | }, 18 | timing: { 19 | type: String, 20 | required: true 21 | }, 22 | status: { 23 | type: String, 24 | required: true 25 | }, 26 | specialists: { 27 | type: String 28 | }, 29 | ruppess: { 30 | type: String 31 | }, 32 | bio: { 33 | type: String 34 | }, 35 | patients: [ 36 | { 37 | user: { 38 | type: Schema.Types.ObjectId, 39 | ref: 'users' 40 | }, 41 | bookingId: { 42 | type: Number 43 | }, 44 | patientname: { 45 | type: String, 46 | required: true 47 | }, 48 | fathername: { 49 | type: String, 50 | }, 51 | status: { 52 | type: String, 53 | }, 54 | age: { 55 | type: Number, 56 | 57 | }, 58 | date: { 59 | type: Date, 60 | }, 61 | description: { 62 | type: String, 63 | required: true 64 | }, 65 | name: { 66 | type: String 67 | }, 68 | avatar: { 69 | type: String 70 | }, 71 | date: { 72 | type: Date, 73 | default: Date.now 74 | } 75 | } 76 | ], 77 | review: [ 78 | { 79 | user: { 80 | type: Schema.Types.ObjectId, 81 | ref: 'users' 82 | }, 83 | text: { 84 | type: String, 85 | require: true 86 | }, 87 | name: { 88 | type: String 89 | }, 90 | avatar: { 91 | type: String 92 | }, 93 | date: { 94 | type: Date, 95 | default: Date.now 96 | } 97 | } 98 | ], 99 | experience : [ 100 | { 101 | position: { 102 | type: String, 103 | required: true 104 | }, 105 | medical: { 106 | type: String, 107 | required: true 108 | }, 109 | location: { 110 | type: String 111 | }, 112 | from: { 113 | type: Date, 114 | required: true 115 | }, 116 | to: { 117 | type: Date 118 | }, 119 | current: { 120 | type: Boolean, 121 | default: false 122 | }, 123 | description: { 124 | type: String 125 | } 126 | } 127 | ], 128 | education: [ 129 | { 130 | school: { 131 | type: String, 132 | required: true 133 | }, 134 | degree: { 135 | type: String, 136 | required: true 137 | }, 138 | fieldofstudy: { 139 | type: String, 140 | required: true 141 | }, 142 | from: { 143 | type: Date, 144 | required: true 145 | }, 146 | to: { 147 | type: Date 148 | }, 149 | current: { 150 | type: Boolean, 151 | default: false 152 | }, 153 | description: { 154 | type: String 155 | } 156 | } 157 | ], 158 | social: { 159 | youtube: { 160 | type: String 161 | }, 162 | twitter: { 163 | type: String 164 | }, 165 | facebook: { 166 | type: String 167 | }, 168 | instagram: { 169 | type: String 170 | } 171 | }, 172 | date: { 173 | type: Date, 174 | default: Date.now 175 | } 176 | }); 177 | 178 | module.exports = Profile = mongoose.model('profile', ProfileSchema); 179 | -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require ('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const UserSchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true 8 | }, 9 | email: { 10 | type: String, 11 | required: true, 12 | unique: true 13 | }, 14 | password: { 15 | type: String, 16 | required: true 17 | }, 18 | avatar: { 19 | type: String 20 | }, 21 | appointments: [ 22 | { 23 | doctor: { 24 | type: Schema.Types.ObjectId, 25 | ref: 'doctors' 26 | }, 27 | bookingId: { 28 | type: Number 29 | }, 30 | patientname: { 31 | type: String, 32 | required: true 33 | }, 34 | fathername: { 35 | type: String, 36 | }, 37 | status: { 38 | type: String, 39 | }, 40 | age: { 41 | type: Number, 42 | }, 43 | date: { 44 | type: Date, 45 | }, 46 | description: { 47 | type: String, 48 | required: true 49 | }, 50 | name: { 51 | type: String 52 | }, 53 | avatar: { 54 | type: String 55 | }, 56 | date: { 57 | type: Date, 58 | default: Date.now 59 | } 60 | } 61 | ], 62 | date: { 63 | type: Date, 64 | default: Date.now 65 | } 66 | }); 67 | 68 | module.exports = User = mongoose.model('user', UserSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doctor_connector", 3 | "version": "1.0.0", 4 | "description": "By Jeevan Jyoti Dash", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "server": "nodemon server.js", 9 | "client": "npm start --prefix client", 10 | "dev": "concurrently \"npm run server\" \"npm run client\"" 11 | }, 12 | "author": "JeevanTheDev", 13 | "license": "ISC", 14 | "dependencies": { 15 | "bcryptjs": "^2.4.3", 16 | "body-parser": "^1.19.0", 17 | "bootstrap": "^4.5.0", 18 | "chart.js": "^2.9.3", 19 | "config": "^3.3.1", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "express-validator": "^6.6.0", 23 | "gravatar": "^1.8.0", 24 | "jsonwebtoken": "^8.5.1", 25 | "moment": "^2.27.0", 26 | "mongoose": "^5.9.21", 27 | "passport": "^0.4.1", 28 | "passport-jwt": "^4.0.0", 29 | "react-bootstrap": "^1.1.1", 30 | "react-chartjs-2": "^2.9.0", 31 | "react-router-redux": "^4.0.8", 32 | "redux": "^4.0.5", 33 | "request": "^2.88.2", 34 | "validator": "^13.1.1" 35 | }, 36 | "devDependencies": { 37 | "concurrently": "^5.2.0", 38 | "nodemon": "^2.0.4" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /routes/api/appointment.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const { check, validationResult } = require('express-validator'); 5 | 6 | const authUser = require('../../middleware/authUser'); 7 | 8 | const User = require('../../models/User'); 9 | const Doctor = require('../../models/Doctor'); 10 | const Profile = require('../../models/Profile'); 11 | 12 | 13 | // @route Post api/appointment/:doctor_id 14 | // @desc Create a user appointment 15 | // @access Private 16 | router.post('/:doctor_id', [authUser, 17 | [ 18 | check('patientname', 'Patient name is required') 19 | .not() 20 | .isEmpty(), 21 | check('fathername', 'Father name is required') 22 | .not() 23 | .isEmpty(), 24 | check('age', 'Age is required') 25 | .not() 26 | .isEmpty(), 27 | check('description', 'Description is required') 28 | .not() 29 | .isEmpty() 30 | ] 31 | ], async (req, res) => { 32 | const errors = validationResult(req); 33 | if(!errors.isEmpty()) { 34 | return res.status(400).json({ errors: errors.array() }); 35 | } 36 | try { 37 | const user = await User.findById(req.user.id).select('-password'); 38 | const doctor = await Doctor.findById(req.params.doctor_id).select('-password'); 39 | const profile = await Profile.findOne({ doctor: req.params.doctor_id }); 40 | 41 | // Create booking id 42 | function appointmentGenerator() { 43 | 44 | this.length = 8; 45 | this.timestamp = +new Date; 46 | 47 | var _getRandomInt = function( min, max ) { 48 | return Math.floor( Math.random() * ( max - min + 1 ) ) + min; 49 | } 50 | 51 | this.generate = function() { 52 | var ts = this.timestamp.toString(); 53 | var parts = ts.split( "" ).reverse(); 54 | var id = ""; 55 | 56 | for( var i = 0; i < this.length; ++i ) { 57 | var index = _getRandomInt( 0, parts.length - 1 ); 58 | id += parts[index]; 59 | } 60 | 61 | return id; 62 | } 63 | } 64 | 65 | const create_id = new appointmentGenerator(); 66 | const appointmentId = create_id.generate(); 67 | 68 | const newPatient = { 69 | bookingId: appointmentId, 70 | patientname: req.body.patientname, 71 | fathername: req.body.fathername, 72 | status: req.body.status, 73 | age: req.body.age, 74 | date: req.body.date, 75 | description: req.body.description, 76 | avatar: user.avatar, 77 | name: user.name, 78 | user: req.user.id 79 | } 80 | 81 | const newAppointment = { 82 | bookingId: appointmentId, 83 | patientname: req.body.patientname, 84 | fathername: req.body.fathername, 85 | status: req.body.status, 86 | age: req.body.age, 87 | date: req.body.date, 88 | description: req.body.description, 89 | avatar: doctor.avatar, 90 | name: doctor.name, 91 | doctor: doctor.id 92 | } 93 | 94 | profile.patients.unshift(newPatient); 95 | 96 | await profile.save(); 97 | 98 | user.appointments.unshift(newAppointment); 99 | await user.save(); 100 | 101 | res.json(user); 102 | } catch (err) { 103 | console.error(err.message); 104 | res.status(500).send("Server error"); 105 | } 106 | }); 107 | 108 | // // @route Delete api/appointment/:appointment_id 109 | // // @desc Delete a appointment 110 | // // @access Private 111 | // router.delete('/:appointment_id', authUser, async (req, res) => { 112 | // try { 113 | // const user = await User.findById(req.user.id).select('-password'); 114 | 115 | // const doctorId = user.appointments 116 | // .map(item => { 117 | // if(item.id === req.params.appointment_id) { 118 | // return item.doctor; 119 | // } 120 | // }) 121 | // const profile = await Profile.findOne({ doctor: doctorId[0]}); 122 | 123 | // // Get the remove index for user 124 | // const removeIndexUser = user.appointments 125 | // .map(item => item.id) 126 | // .indexOf(req.params.appointment_id); 127 | 128 | // user.appointments.splice(removeIndexUser, 1); 129 | // await user.save(); 130 | 131 | // // Get the remove index for doctor 132 | // const removeIndexDoctor = profile.patients 133 | // .map(item => item.id) 134 | // .indexOf(req.params.appointment_id); 135 | 136 | // profile.patients.splice(removeIndexDoctor, 1); 137 | // await profile.save(); 138 | 139 | // // Return user 140 | // res.json(user); 141 | // } catch (err) { 142 | 143 | // } 144 | // }); 145 | 146 | 147 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/authDoctor.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const { check, validationResult } = require('express-validator'); 5 | const jwt = require('jsonwebtoken'); 6 | const bcrypt = require('bcryptjs'); 7 | const authDoctor = require('../../middleware/authDoctor'); 8 | 9 | const Doctor = require('../../models/Doctor'); 10 | 11 | // @route GET api/authDoctor 12 | // @desc Tests auth Doctor route 13 | // @access Public 14 | router.get('/', authDoctor, async(req, res) => { 15 | try { 16 | const doctor = await Doctor.findById(req.doctor.id).select('-password'); 17 | res.json(doctor); 18 | } catch (err) { 19 | console.error(err.message); 20 | res.status(500).send("Server Error"); 21 | } 22 | }); 23 | 24 | // @route post api/authDoctor 25 | // @desc Authenticate Doctor and get Token 26 | // @access Public 27 | router.post('/', 28 | [ 29 | check('email', 'Please include a valid email').isEmail(), 30 | check('password', 'Password required').exists() 31 | ], 32 | async (req, res) => { 33 | const errors = validationResult(req); 34 | if(!errors.isEmpty()) { 35 | return res.status(400).json({ errors: errors.array() }); 36 | } 37 | 38 | const {email, password} = req.body; 39 | 40 | try { 41 | // If Doctor exists 42 | let doctor = await Doctor.findOne({ email }); 43 | 44 | if(!doctor) { 45 | return res.status(400).json({ errors: [{ msg: 'Invalid cridentials' }] }); 46 | } 47 | 48 | const isMatch = await bcrypt.compare(password, doctor.password); 49 | 50 | if(!isMatch) { 51 | return res.status(400).json({ errors: [{ msg: 'Invalid cridentials' }] }); 52 | } 53 | 54 | // Return jsonwebtoken 55 | 56 | const payload = { 57 | doctor: { 58 | id: doctor.id 59 | } 60 | } 61 | 62 | jwt.sign(payload, process.env.jwtSecret, 63 | {expiresIn: 36000}, 64 | (err, token) => { 65 | if(err) throw err; 66 | res.json({ token }); 67 | }); 68 | 69 | } catch (err) { 70 | console.error(err.message); 71 | res.status(500).send('Server error'); 72 | } 73 | }); 74 | 75 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/authUser.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const { check, validationResult } = require('express-validator'); 5 | const jwt = require('jsonwebtoken'); 6 | const bcrypt = require('bcryptjs'); 7 | const authUser = require('../../middleware/authUser'); 8 | 9 | const User = require('../../models/User'); 10 | const Doctor = require('../../models/Doctor'); 11 | const Profile = require('../../models/Profile'); 12 | 13 | // @route GET api/authUser 14 | // @desc Tests auth User route 15 | // @access Public 16 | router.get('/', authUser, async(req, res) => { 17 | try { 18 | const user = await User.findById(req.user.id).select('-password'); 19 | res.json(user); 20 | } catch (err) { 21 | console.error(err.message); 22 | res.status(500).send("Server Error"); 23 | } 24 | }); 25 | 26 | // @route post api/authUser 27 | // @desc Authenticate User and get Token 28 | // @access Public 29 | router.post('/', 30 | [ 31 | check('email', 'Please include a valid email').isEmail(), 32 | check('password', 'Password required').exists() 33 | ], 34 | async (req, res) => { 35 | const errors = validationResult(req); 36 | if(!errors.isEmpty()) { 37 | return res.status(400).json({ errors: errors.array() }); 38 | } 39 | 40 | const {email, password} = req.body; 41 | 42 | try { 43 | // If User exists 44 | let user = await User.findOne({ email }); 45 | 46 | if(!user) { 47 | return res.status(400).json({ errors: [{ msg: 'Invalid cridentials' }] }); 48 | } 49 | 50 | const isMatch = await bcrypt.compare(password, user.password); 51 | 52 | if(!isMatch) { 53 | return res.status(400).json({ errors: [{ msg: 'Invalid cridentials' }] }); 54 | } 55 | 56 | // Return jsonwebtoken 57 | 58 | const payload = { 59 | user: { 60 | id: user.id 61 | } 62 | } 63 | 64 | jwt.sign(payload, process.env.jwtSecret, 65 | {expiresIn: 36000}, 66 | (err, token) => { 67 | if(err) throw err; 68 | res.json({ token }); 69 | }); 70 | 71 | } catch (err) { 72 | console.error(err.message); 73 | res.status(500).send('Server error'); 74 | } 75 | }); 76 | 77 | // @route Delete api/authUser/:appointment_id 78 | // @desc Delete a appointment 79 | // @access Private 80 | router.delete('/:appointment_id', authUser, async (req, res) => { 81 | try { 82 | const user = await User.findById(req.user.id).select('-password'); 83 | 84 | const doctorId = user.appointments 85 | .map(item => ( 86 | item.id === req.params.appointment_id 87 | ) ? item.doctor : null 88 | ); 89 | 90 | const newId = doctorId.filter( doctor => doctor !== null); 91 | 92 | const profile = await Profile.findOne({ doctor: newId[0]}); 93 | 94 | // Get the remove index for user 95 | const removeIndexUser = user.appointments 96 | .map(item => item.id) 97 | .indexOf(req.params.appointment_id); 98 | 99 | const removeIndex = user.appointments[removeIndexUser].bookingId; 100 | user.appointments.splice(removeIndexUser, 1); 101 | 102 | await user.save(); 103 | 104 | if(profile !== null) { 105 | const indexDoctor = profile.patients 106 | .map(item => item.bookingId) 107 | .indexOf(removeIndex); 108 | 109 | profile.patients.splice(indexDoctor, 1); 110 | 111 | await profile.save(); 112 | 113 | } else { 114 | console.log("No Dcotor exist"); 115 | } 116 | 117 | // Return user 118 | res.json(user); 119 | } catch (err) { 120 | console.error(err.message); 121 | res.status(500).send("Server error"); 122 | } 123 | }); 124 | 125 | // @route Delete api/authUser 126 | // @desc Delete profile, doctor 127 | // @access Private 128 | router.delete('/', authUser, async (req, res) => { 129 | try { 130 | // Remove user 131 | await User.findOneAndRemove({ _id: req.user.id }); 132 | res.json({ msg: "User deleted" }); 133 | } catch (err) { 134 | console.error(err.message); 135 | res.status(500).send('Server error'); 136 | } 137 | }); 138 | 139 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/doctors.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const gravtar = require('gravatar'); 5 | const { check, validationResult } = require('express-validator'); 6 | const bcrypt = require('bcryptjs'); 7 | const jwt = require('jsonwebtoken'); 8 | const Doctor = require('../../models/Doctor'); 9 | 10 | // @route Post api/doctors 11 | // @desc Register Doctor 12 | // @access Public 13 | router.post('/', 14 | [ 15 | check('name', 'Name is required') 16 | .not() 17 | .isEmpty(), 18 | check('email', 'Please include a valid email').isEmail(), 19 | check('password', 'Please enter a password min 6 to 10 charcters').isLength({ min: 6 }) 20 | ], 21 | async (req, res) => { 22 | const errors = validationResult(req); 23 | if(!errors.isEmpty()) { 24 | return res.status(400).json({ errors: errors.array() }); 25 | } 26 | const { name, email, password } = req.body; 27 | 28 | try { 29 | // See if user exists 30 | let doctor = await Doctor.findOne({ email }); 31 | 32 | if(doctor) { 33 | return res.status(400).json({ errors: [{ msg: 'Doctor already exists' }] }); 34 | } 35 | 36 | // Get doctors gravtar 37 | 38 | const avatar = gravtar.url(email, { 39 | s: '200', 40 | r: 'pg', 41 | d: 'mm' 42 | }); 43 | 44 | doctor = new Doctor({ 45 | name, 46 | email, 47 | avatar, 48 | password 49 | }); 50 | 51 | // Encrypt password 52 | 53 | const salt = await bcrypt.genSalt(10); 54 | 55 | doctor.password = await bcrypt.hash(password, salt); 56 | 57 | await doctor.save(); 58 | 59 | //Return jsonwebtoken 60 | 61 | const payload = { 62 | doctor: { 63 | id: doctor.id 64 | } 65 | } 66 | 67 | jwt.sign(payload, process.env.jwtSecret, 68 | { expiresIn: 360000 }, 69 | (err, token) => { 70 | if (err) throw err; 71 | res.json({ token }); 72 | }); 73 | 74 | } catch (err) { 75 | console.error(err.message); 76 | res.status(500).send('Server error'); 77 | } 78 | }); 79 | 80 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/profile.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const request = require('request'); 4 | const router = express.Router(); 5 | const authDoctor = require('../../middleware/authDoctor'); 6 | const authUser = require('../../middleware/authUser'); 7 | const { check, validationResult} = require('express-validator'); 8 | 9 | const Doctor = require('../../models/Doctor'); 10 | const User = require('../../models/User'); 11 | const Profile = require('../../models/Profile'); 12 | 13 | // @route GET api/profile/me 14 | // @desc Get current doctor profile 15 | // @access Private 16 | router.get('/me', authDoctor, async (req, res) => { 17 | try { 18 | const profile = await Profile.findOne({ doctor: req.doctor.id }).populate('doctor', ['name', 'avatar']); 19 | if(!profile) { 20 | return res.status(400).json({ msg: "No Profile for this doctor "}); 21 | } 22 | 23 | res.json(profile); 24 | } catch (err) { 25 | console.error(err.message); 26 | res.status(500).send("Server error"); 27 | } 28 | }); 29 | 30 | // @route Post api/profile 31 | // @desc Create or update a doctor profile 32 | // @access Private 33 | router.post('/', 34 | [ 35 | authDoctor, 36 | [ 37 | check('status', 'Status is required') 38 | .not() 39 | .isEmpty() 40 | ] 41 | ], 42 | async (req, res) => { 43 | const errors = validationResult(req); 44 | if(!errors.isEmpty()) { 45 | return res.status(400).json({ errors: errors.array() }); 46 | } 47 | 48 | const { 49 | clinic, 50 | website, 51 | location, 52 | timing, 53 | bio, 54 | status, 55 | specialists, 56 | ruppess, 57 | youtube, 58 | facebook, 59 | twitter, 60 | instagram, 61 | } = req.body; 62 | 63 | // Build profile object 64 | const profileFields = {}; 65 | profileFields.doctor = req.doctor.id; 66 | if(clinic) profileFields.clinic = clinic; 67 | if(website) profileFields.website = website; 68 | if(location) profileFields.location = location; 69 | if(timing) profileFields.timing = timing; 70 | if(bio) profileFields.bio = bio; 71 | if(status) profileFields.status = status; 72 | if(specialists) profileFields.specialists = specialists; 73 | if(ruppess) profileFields.ruppess = ruppess; 74 | 75 | //Build social objects 76 | profileFields.social = {} 77 | if (youtube) profileFields.social.youtube = req.body.youtube; 78 | if (twitter) profileFields.social.twitter = req.body.twitter; 79 | if (facebook) profileFields.social.facebook = req.body.facebook; 80 | if (instagram) profileFields.social.instagram = req.body.instagram; 81 | 82 | try { 83 | let profile = await Profile.findOne({ doctor: req.doctor.id }); 84 | 85 | if(profile) { 86 | // Update 87 | profile = await Profile.findOneAndUpdate( 88 | { doctor: req.doctor.id }, 89 | { $set: profileFields }, 90 | { new: true } 91 | ); 92 | 93 | return res.json(profile); 94 | } 95 | 96 | // Create 97 | profile = new Profile(profileFields); 98 | await profile.save(); 99 | res.json(profile); 100 | } catch (err) { 101 | console.error(err.message); 102 | res.status(500).send("Server error"); 103 | } 104 | }); 105 | 106 | // @route Get api/profile 107 | // @desc Get all profiles 108 | // @access Public 109 | 110 | router.get('/', async (req, res) => { 111 | try { 112 | const profiles = await Profile.find().populate('doctor', ['name', 'avatar']); 113 | res.json(profiles); 114 | } catch (err) { 115 | console.error(err.message); 116 | res.status(500).send('Server error'); 117 | 118 | } 119 | }); 120 | 121 | // @route Get api/profile/doctor/:doctor_id 122 | // @desc Get profile by doctor id 123 | // @access Public 124 | 125 | router.get('/doctor/:doctor_id', async (req, res) => { 126 | try { 127 | const profile = await Profile.findOne({ doctor: req.params.doctor_id }).populate('doctor', ['name', 'avatar']); 128 | if(!profile) { 129 | return res.status(400).json({ msg: "There is no profile" }); 130 | } 131 | res.json(profile); 132 | } catch (err) { 133 | console.error(err.message); 134 | if(err.kind === 'ObjectId') { 135 | return res.status(400).json({ msg: "There is no profile" }); 136 | } 137 | res.status(500).send('Server error'); 138 | } 139 | }); 140 | 141 | // @route Post api/profile/doctor/:doctor_id 142 | // @desc Comment on a profile 143 | // @access Private 144 | router.post('/doctor/:doctor_id', [authUser, 145 | [ 146 | check('text', 'Text is required').not().isEmpty() 147 | ] 148 | ], async (req, res) => { 149 | const errors = validationResult(req); 150 | if(!errors.isEmpty()) { 151 | return res.status(400).json({ errors: errors.array() }); 152 | } 153 | try { 154 | const user = await User.findById(req.user.id).select('-password'); 155 | const profile = await Profile.findOne({ doctor: req.params.doctor_id }); 156 | 157 | const newReview = { 158 | text: req.body.text, 159 | name: user.name, 160 | avatar: user.avatar, 161 | user: req.user.id 162 | }; 163 | 164 | profile.review.unshift(newReview); 165 | 166 | await profile.save(); 167 | res.json(profile); 168 | } catch (err) { 169 | console.error(err.message); 170 | res.status(500).send("Server error"); 171 | } 172 | }); 173 | 174 | // @route Delete api/profile/doctor/:doctor_id/:review_id 175 | // @desc Delete comment on a profile 176 | // @access Private 177 | router.delete('/doctor/:doctor_id/:review_id', authUser, async (req, res) => { 178 | try { 179 | const profile = await Profile.findOne({ doctor: req.params.doctor_id }); 180 | 181 | // Pill out review 182 | const review = profile.review.find(review => review.id === req.params.review_id); 183 | 184 | const text = review.text; 185 | 186 | // Make sure review exists 187 | if(!review) { 188 | return res.status(404).json({ msg: "Review not exist "}); 189 | } 190 | // Check user 191 | if(review.user.toString() !== req.user.id) { 192 | return res.status(404).json({ msg: "User not autherized" }); 193 | } 194 | 195 | // get remove index 196 | const removeIndex = profile.review.map( review => review.text).indexOf(text); 197 | 198 | profile.review.splice(removeIndex, 1); 199 | 200 | await profile.save(); 201 | res.json(profile); 202 | } catch (err) { 203 | console.error(err.message); 204 | res.status(500).send("Server error"); 205 | } 206 | }); 207 | 208 | // @route Delete api/profile 209 | // @desc Delete profile, doctor 210 | // @access Private 211 | router.delete('/', authDoctor, async (req, res) => { 212 | try { 213 | // Remove profile 214 | await Profile.findOneAndRemove({ doctor: req.doctor.id }); 215 | // Remove doctor 216 | await Doctor.findOneAndRemove({ _id: req.doctor.id }); 217 | res.json({ msg: "Doctor deleted" }); 218 | } catch (err) { 219 | console.error(err.message); 220 | res.status(500).send('Server error'); 221 | } 222 | }); 223 | 224 | // @route Put api/profile/experience 225 | // @desc Add profile, experience 226 | // @access Private 227 | router.put('/experience', [authDoctor, 228 | [ 229 | check('position', 'Position is required') 230 | .not() 231 | .isEmpty(), 232 | check('medical', 'Medical is required') 233 | .not() 234 | .isEmpty(), 235 | check('from', 'From date is required') 236 | .not() 237 | .isEmpty() 238 | ] 239 | ], async (req, res) => { 240 | const errors = validationResult(req); 241 | if(!errors.isEmpty()) { 242 | return res.status(400).json({ errors: errors.array() }); 243 | } 244 | 245 | const { 246 | position, 247 | medical, 248 | location, 249 | from, 250 | to, 251 | current, 252 | description 253 | } = req.body; 254 | 255 | const newExp = { 256 | position, 257 | medical, 258 | location, 259 | from, 260 | to, 261 | current, 262 | description 263 | } 264 | try { 265 | const profile = await Profile.findOne({ doctor: req.doctor.id }); 266 | profile.experience.unshift(newExp); 267 | await profile.save(); 268 | 269 | res.json(profile); 270 | } catch (err) { 271 | console.error(err.message); 272 | res.status(500).send('Server error'); 273 | } 274 | }); 275 | 276 | // @route Delete api/profile/experience/:exp_id 277 | // @desc Delete experience from profile 278 | // @access Private 279 | router.delete('/experience/:exp_id', authDoctor, async (req, res) => { 280 | try { 281 | const profile = await Profile.findOne({ doctor: req.doctor.id }); 282 | // Get remove index 283 | const removeIndex = profile.experience 284 | .map(item => item.id) 285 | .indexOf(req.params.exp_id); 286 | 287 | profile.experience.splice(removeIndex, 1); 288 | await profile.save(); 289 | res.json(profile); 290 | 291 | } catch (err) { 292 | console.error(err.message); 293 | res.status(500).send('Server error'); 294 | } 295 | }); 296 | 297 | // @route PUT api/profile/education 298 | // @desc Add profile education 299 | // @access Private 300 | router.put('/education', [ 301 | authDoctor, 302 | [ 303 | check('school', 'School is required').not().isEmpty(), 304 | check('degree', 'Degree is required').not().isEmpty(), 305 | check('fieldofstudy', 'Field of study is required').not().isEmpty(), 306 | check('from', 'From date is required and needs to be from the past') 307 | .not() 308 | .isEmpty() 309 | ] 310 | ], async (req, res) => { 311 | const errors = validationResult(req); 312 | if(!errors.isEmpty()) { 313 | return res.status(400).json({ errors: errors.array() }); 314 | } 315 | 316 | const { 317 | school, 318 | degree, 319 | fieldofstudy, 320 | from, 321 | to, 322 | current, 323 | description 324 | } = req.body; 325 | 326 | const newEdu = { 327 | school, 328 | degree, 329 | fieldofstudy, 330 | from, 331 | to, 332 | current, 333 | description 334 | }; 335 | 336 | try { 337 | const profile = await Profile.findOne({ doctor: req.doctor.id }); 338 | 339 | profile.education.unshift(newEdu); 340 | 341 | await profile.save(); 342 | 343 | res.json(profile); 344 | } catch (err) { 345 | console.error(err.message); 346 | res.status(500).send('Server Error'); 347 | } 348 | }); 349 | 350 | // @route DELETE api/profile/education/:edu_id 351 | // @desc Delete education from profile 352 | // @access Private 353 | 354 | router.delete('/education/:edu_id', authDoctor, async (req, res) => { 355 | try { 356 | const foundProfile = await Profile.findOne({ doctor: req.doctor.id }); 357 | foundProfile.education = foundProfile.education.filter( 358 | (edu) => edu._id.toString() !== req.params.edu_id 359 | ); 360 | await foundProfile.save(); 361 | return res.status(200).json(foundProfile); 362 | } catch (error) { 363 | console.error(error); 364 | return res.status(500).json({ msg: 'Server error' }); 365 | } 366 | }); 367 | 368 | module.exports = router; -------------------------------------------------------------------------------- /routes/api/users.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const router = express.Router(); 4 | const gravtar = require('gravatar'); 5 | const { check, validationResult } = require('express-validator'); 6 | const bcrypt = require('bcryptjs'); 7 | const jwt = require('jsonwebtoken'); 8 | 9 | const User = require('../../models/User'); 10 | 11 | // @route Post api/user 12 | // @desc Register USer 13 | // @access Public 14 | router.post('/', 15 | [ 16 | check('name', 'Name is required') 17 | .not() 18 | .isEmpty(), 19 | check('email', 'Please include a valid email').isEmail(), 20 | check('password', 'Please enter a password min 6 to 10 charcters').isLength({ min: 6 }) 21 | ], 22 | async (req, res) => { 23 | const errors = validationResult(req); 24 | if(!errors.isEmpty()) { 25 | return res.status(400).json({ errors: errors.array() }); 26 | } 27 | const { name, email, password } = req.body; 28 | 29 | try { 30 | // See if user exists 31 | let user = await User.findOne({ email }); 32 | 33 | if(user) { 34 | return res.status(400).json({ errors: [{ msg: 'User already exists' }] }); 35 | } 36 | 37 | // Get User gravtar 38 | 39 | const avatar = gravtar.url(email, { 40 | s: '200', 41 | r: 'pg', 42 | d: 'mm' 43 | }); 44 | 45 | user = new User({ 46 | name, 47 | email, 48 | avatar, 49 | password 50 | }); 51 | 52 | // Encrypt password 53 | 54 | const salt = await bcrypt.genSalt(10); 55 | 56 | user.password = await bcrypt.hash(password, salt); 57 | 58 | await user.save(); 59 | 60 | //Return jsonwebtoken 61 | 62 | const payload = { 63 | user: { 64 | id: user.id 65 | } 66 | } 67 | 68 | jwt.sign(payload, process.env.jwtSecret, 69 | { expiresIn: 360000 }, 70 | (err, token) => { 71 | if (err) throw err; 72 | res.json({ token }); 73 | }); 74 | 75 | } catch (err) { 76 | console.error(err.message); 77 | res.status(500).send('Server error'); 78 | } 79 | }); 80 | 81 | module.exports = router; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const mongoose = require("mongoose"); 3 | const doctors = require('./routes/api/doctors'); 4 | const authsDoctor = require('./routes/api/authDoctor'); 5 | const users = require('./routes/api/users'); 6 | const authsUser = require('./routes/api/authUser'); 7 | const profile = require('./routes/api/profile'); 8 | const appointment = require('./routes/api/appointment'); 9 | 10 | const app = express(); 11 | 12 | // DB config 13 | const db = require('./config/keys').mongoURI; 14 | 15 | // connect to MongoDB; 16 | mongoose 17 | .connect(db, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false }) 18 | .then(() => console.log("MongoDB Connected")) 19 | .catch(err => console.log(err)); 20 | 21 | //Init Middleware 22 | app.use(express.json({ extended: false})); 23 | 24 | app.get('/', (req, res) => res.send("Welcome Jeevan Joti Dash")); 25 | 26 | // Use Routes 27 | app.use('/api/doctors', doctors); 28 | app.use('/api/authDoctor', authsDoctor); 29 | app.use('/api/users', users); 30 | app.use('/api/authUser', authsUser); 31 | app.use('/api/profile', profile); 32 | app.use('/api/appointment', appointment); 33 | 34 | 35 | 36 | const port = process.env.PORT || 5000; 37 | app.listen(port , () => console.log(`Server running on port ${port}`)); --------------------------------------------------------------------------------