diff --git a/client/src/components/tutorComponents/ProfilePicModal.tsx b/client/src/components/tutorComponents/ProfilePicModal.tsx
new file mode 100644
index 0000000..9617a14
--- /dev/null
+++ b/client/src/components/tutorComponents/ProfilePicModal.tsx
@@ -0,0 +1,88 @@
+import React from "react";
+import { api } from "../../services/api";
+import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from "reactstrap";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faEdit } from "@fortawesome/free-solid-svg-icons";
+import Avatar from "react-avatar-edit";
+import "../../containers/DashboardPage/ClientSettings.css";
+import defaultUser from "../../assets/default_user.png";
+import { CompressAndSaveImg } from "../../services/tools";
+
+
+ export interface IProfilePicModalProps {
+ isTutor:boolean,
+ firstName:string,
+ lastName:string,
+ clientImg:string,
+ imgModalOpen:boolean,
+ croppedImg:string,
+ setImgModalOpen: (arg:boolean) => void,
+ cancelImgChange: () => void,
+ setCroppedImg: (arg:string) =>void,
+ setClientImg: (arg:string) =>void,
+ userid:string,
+}
+
+export const ProfilePicModal = (props:IProfilePicModalProps) => {
+
+ const {clientImg,imgModalOpen,croppedImg,userid} = props;
+ const {setImgModalOpen,cancelImgChange,setCroppedImg,setClientImg} = props;
+
+ const handleImageSave = async (img: string) => {
+
+ if (props.isTutor === true){
+ console.log(userid)
+ await api.SetTutorProfileImage(img, userid);
+ }
+ else{
+
+ await api.SetClientProfileImage(img, userid);
+
+ }
+ setClientImg(img);
+ }
+
+ const saveImgChange = async () => {
+ if(croppedImg.toString() !== "") {
+ CompressAndSaveImg(croppedImg, props.firstName + props.lastName + "-photo", handleImageSave);
+ } else {
+ handleImageSave(croppedImg);
+ }
+
+ setImgModalOpen(false);
+ }
+ return (
+ <>
+
+ setImgModalOpen(true)}>
+
+
+ {setImgModalOpen(!imgModalOpen)}} className="img-modal">
+ {cancelImgChange()}}>Edit Profile Photo
+
+ Change your profile photo here.
+
+ setCroppedImg(img)}
+ onClose={() => {setCroppedImg("")}}
+ onBeforeFileLoad={() => {}}
+ src={clientImg}
+ />
+
+
+
+
+
+
+ >
+
+
+ );
+
+
+}
\ No newline at end of file
diff --git a/client/src/components/tutorComponents/Settings.tsx b/client/src/components/tutorComponents/Settings.tsx
index 11d96bd..d0f9396 100644
--- a/client/src/components/tutorComponents/Settings.tsx
+++ b/client/src/components/tutorComponents/Settings.tsx
@@ -3,8 +3,13 @@ import classNames from "classnames";
import { Container, Row, Col, ListGroup, ListGroupItem, Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, InputGroup, Input, Alert, Nav, NavItem, NavLink, TabContent, TabPane } from "reactstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEdit, faBan, faPlus, faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
+import {ProfilePicModal,IProfilePicModalProps} from './ProfilePicModal'
+import { TutorDataSlice } from "../../store/TutorData/types";
+import { ClientDataSlice } from "../../store/ClientData/types";
+import { connect } from 'react-redux';
import Slider from 'react-rangeslider';
import Autocomplete from 'react-autocomplete';
+import defaultUser from "../../assets/default_user.png";
import TimePicker from 'rc-time-picker';
import moment from 'moment';
import Cookies from "js-cookie";
@@ -15,7 +20,8 @@ import "./settings.css";
import 'rc-time-picker/assets/index.css';
interface iSettingsProps {
-
+ clientData:ClientDataSlice,
+ tutorData: TutorDataSlice
}
interface iSettingsState {
@@ -49,11 +55,14 @@ interface iSettingsState {
interval_modal: boolean,
schedule_modal: boolean,
add_time_err: boolean,
- add_time_err_msg: string
+ add_time_err_msg: string,
+ imgModalOpen:boolean,
+ croppedImg:string
}
+//to-do: connect to Redux store Client + Tutor Data slice
class Settings extends Component {
constructor(props: iSettingsProps) {
@@ -65,7 +74,7 @@ class Settings extends Component {
temp_firstn: "",
temp_lastn: "",
email: "test2@gmail.com",
- obj_id: userid !== undefined ? userid : "61a5a9bbc73a5d336d8d0b74",
+ obj_id: userid || "61a5a9bbc73a5d336d8d0b74",
profile_pic: "",
description: "Typescript port",
temp_description: "",
@@ -114,7 +123,9 @@ class Settings extends Component {
interval_modal: false,
schedule_modal: false,
add_time_err: false,
- add_time_err_msg: ""
+ add_time_err_msg: "",
+ imgModalOpen:false,
+ croppedImg:""
} as iSettingsState;
}
@@ -139,7 +150,7 @@ class Settings extends Component {
temp_lastn: tutor.last_name,
meeting_interval: parseInt(tutor.interval),
temp_meeting_interval: parseInt(tutor.interval),
- profile_pic:tutor.profile_img,
+ profile_pic:tutor.profile_img || defaultUser,
description: tutor.description || "",
temp_description: tutor.description || ""
@@ -590,8 +601,26 @@ class Settings extends Component {
return hrs + ":" + mins + meridiem;
}
+
+
+
render(): JSX.Element{
let schedule_days = [{day: "Sunday", abbr: "SUN"}, {day: "Monday", abbr: "MON"}, {day: "Tuesday", abbr: "TUE"}, {day: "Wednesday", abbr: "WED"}, {day: "Thursday", abbr: "THU"}, {day: "Friday", abbr: "FRI"}, {day: "Saturday", abbr: "SAT"}];
+
+ const profilePicProps = {
+ isTutor:true,
+ firstName:this.state.first_name,
+ lastName:this.state.last_name,
+ clientImg:this.state.profile_pic,
+ imgModalOpen:this.state.imgModalOpen,
+ croppedImg:this.state.croppedImg,
+ setImgModalOpen: (arg:boolean) => this.setState({...this.state,imgModalOpen:arg}),
+ setCroppedImg: (arg:string) =>this.setState({...this.state,croppedImg:arg}),
+ setClientImg: (arg:string) =>this.setState({...this.state,profile_pic:arg}),
+ cancelImgChange: () => this.setState({...this.state,croppedImg:"",imgModalOpen:false}),
+ userid:this.state.obj_id
+
+ } as IProfilePicModalProps;
return (
@@ -601,9 +630,14 @@ class Settings extends Component {
-
+ {/*
+ */}
+
+
+
+
{this.state.first_name + " " + this.state.last_name}
@@ -873,4 +907,11 @@ class Settings extends Component {
}
-export default Settings;
\ No newline at end of file
+function mapStateToProps(state: any) {
+ return {
+ tutorData: state.tutorData,
+ clientData: state.clientData
+ }
+}
+
+export default connect(mapStateToProps)(Settings);
\ No newline at end of file
diff --git a/client/src/containers/DashboardPage/Dashboard.tsx b/client/src/containers/DashboardPage/Dashboard.tsx
index 00bee2b..8245a42 100644
--- a/client/src/containers/DashboardPage/Dashboard.tsx
+++ b/client/src/containers/DashboardPage/Dashboard.tsx
@@ -9,9 +9,7 @@ import { actions as clientDataActions } from "../../store/ClientData/slice";
import { selectClientData } from "../../store/ClientData/selectors";
import {useDispatch, useSelector} from 'react-redux'
import {selectSidebarToggled} from "../../store/ClientFlowData/selectors";
-import TutorPanelBlank from "./TutorPanelSignup";
import { api } from "../../services/api";
-import { Spinner } from "reactstrap";
import TutorPanelSignup from "./TutorPanelSignup";
export interface IParams {
diff --git a/client/src/services/api.ts b/client/src/services/api.ts
index 40c251e..6b8d4a5 100644
--- a/client/src/services/api.ts
+++ b/client/src/services/api.ts
@@ -41,7 +41,7 @@ export class ApiService {
public async GetTutorById(id: String) {
console.log("Fetching Tutor");
- let url = this.tutorsEndpoint + 'tutor/' + id;
+ let url = this.tutorsEndpoint + id;
let response = await axios.get(url);
let tutor: TutorsResponse = {data: []}
tutor.data = response.data;
@@ -197,6 +197,19 @@ export class ApiService {
return await axios.put(url, body, {withCredentials: true});
}
+
+ public async SetTutorProfileImage(img: String, id: String) {
+ let url = this.tutorsEndpoint + 'tutor';
+ let body = {
+ userid: id,
+ profile_img: img
+ }
+
+ return await axios.put(url, body, {withCredentials: true});
+ }
+
+
+
public async SetMeetingLink(id: String, link: String) {
let url = this.appointmentsEndpoint + 'link';
let body = {
diff --git a/server/routes/api/tutors.js b/server/routes/api/tutors.js
index 30c97af..946678a 100644
--- a/server/routes/api/tutors.js
+++ b/server/routes/api/tutors.js
@@ -21,6 +21,9 @@ let router = express.Router();
//Email notify admins of new tutor signup applications
var emailsender = require("../../lib/emailsender.js");
+//dev mode
+const developer = process.env.NODE_ENV !== 'production';
+
//Models
const mongoose = require('mongoose');
const Subject = require('../../models/Subject');
@@ -31,6 +34,10 @@ const TutorApplication = require('../../models/TutorApplication');
// Middleware
const withAuth = require('../../middleware/token_auth')
+const jwt = require('jsonwebtoken');
+const parseCookies = require("../../lib/parseCookies");
+const secret = require("../../config/secret");
+const passport = require('passport');
//mongoose.set('useFindAndModify', false);
@@ -130,6 +137,23 @@ router.post("/", withAuth, (req, res) => {
// PUT /api/tutors/tutor
// Update a tutor
router.put("/tutor", withAuth, (req, res) => {
+
+ const token = parseCookies(req.headers.cookie).token
+
+ //protect route
+ if(!!!developer){
+ jwt.verify(token, secret, function (err, decoded) {
+ if (err) {
+
+ res.status(401).send('Unauthorized: Invalid token');
+ } else if (req.body.userid !== decoded.userid) {
+ res.status(403).send(`Forbidden: Access denied ${req.body.userid} ${decoded.userid}`);
+
+ } else{
+ true;
+ }});
+ }
+
const entries = Object.keys(req.body)
const updates = {}
@@ -159,6 +183,22 @@ router.put("/tutor", withAuth, (req, res) => {
// Update a tutor
router.delete("/:tutor_id", withAuth, (req, res) => {
+ const token = parseCookies(req.headers.cookie).token
+
+ //protect route
+ if(!!!developer){
+ jwt.verify(token, secret, function (err, decoded) {
+ if (err) {
+
+ res.status(401).send('Unauthorized: Invalid token');
+ } else if (req.body.userid !== decoded.userid) {
+ res.status(403).send('Forbidden: Access denied');
+
+ } else{
+ true;
+ }});
+ }
+
Tutor.deleteOne(
{ _id: req.params.tutor_id }
)
diff --git a/server/routes/api/users.js b/server/routes/api/users.js
index 5c7c7b0..4df13db 100644
--- a/server/routes/api/users.js
+++ b/server/routes/api/users.js
@@ -20,10 +20,11 @@ const secret = require('../../config/secret');
* @namespace userRouter
*/
let router = express.Router();
-
+//dev mode
+const developer = process.env.NODE_ENV !== 'production';
// Middleware
const withAuth = require('../../middleware/token_auth');
-
+const parseCookies = require("../../lib/parseCookies");
/**
* Route serving subjects form.
* @name get/api/users
@@ -143,6 +144,23 @@ router.post('/', async (req, res) => {
// PUT /api/users/user
// Update a user
router.put("/user", withAuth, (req, res) => {
+
+ const token = parseCookies(req.headers.cookie).token
+
+ if(!!!developer){
+ jwt.verify(token, secret, function (err, decoded) {
+ console.log(req.body.userid)
+ console.log(decoded.userid)
+ if (err) {
+
+ res.status(401).send('Unauthorized: Invalid token');
+ } else if (req.body.userid !== decoded.userid) {
+ res.status(403).send('Forbidden: Access denied');
+
+ } else{
+ true;
+ }});
+ }
const entries = Object.keys(req.body)
const updates = {}