Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tutorpfpics #106

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions client/src/components/tutorComponents/ProfilePicModal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<img src={clientImg === "" ? defaultUser : clientImg} width="200px"/>
<a href="#" className="modal-link" onClick={() => setImgModalOpen(true)}>
<span className="heading-item"><FontAwesomeIcon icon={faEdit} className="font-adj"/></span>
</a>
<Modal isOpen={imgModalOpen} fade={false} toggle={() => {setImgModalOpen(!imgModalOpen)}} className="img-modal">
<ModalHeader toggle={() => {cancelImgChange()}}>Edit Profile Photo</ModalHeader>
<ModalBody>
Change your profile photo here.
<hr/>
<Avatar
width={250}
height={250}
imageHeight={250}
cropColor="#E66064"
closeIconColor="#E66064"
onCrop={(img) => setCroppedImg(img)}
onClose={() => {setCroppedImg("")}}
onBeforeFileLoad={() => {}}
src={clientImg}
/>
</ModalBody>
<ModalFooter>
<Button className="btn-red" onClick={() => {saveImgChange()}}>Save</Button>
<Button color="secondary" onClick={() => {cancelImgChange()}}>Cancel</Button>
</ModalFooter>
</Modal>
</>


);


}
55 changes: 48 additions & 7 deletions client/src/components/tutorComponents/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -15,7 +20,8 @@ import "./settings.css";
import 'rc-time-picker/assets/index.css';

interface iSettingsProps {

clientData:ClientDataSlice,
tutorData: TutorDataSlice
}

interface iSettingsState {
Expand Down Expand Up @@ -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<iSettingsProps,iSettingsState> {

constructor(props: iSettingsProps) {
Expand All @@ -65,7 +74,7 @@ class Settings extends Component<iSettingsProps,iSettingsState> {
temp_firstn: "",
temp_lastn: "",
email: "[email protected]",
obj_id: userid !== undefined ? userid : "61a5a9bbc73a5d336d8d0b74",
obj_id: userid || "61a5a9bbc73a5d336d8d0b74",
profile_pic: "",
description: "Typescript port",
temp_description: "",
Expand Down Expand Up @@ -114,7 +123,9 @@ class Settings extends Component<iSettingsProps,iSettingsState> {
interval_modal: false,
schedule_modal: false,
add_time_err: false,
add_time_err_msg: ""
add_time_err_msg: "",
imgModalOpen:false,
croppedImg:""
} as iSettingsState;

}
Expand All @@ -139,7 +150,7 @@ class Settings extends Component<iSettingsProps,iSettingsState> {
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 || ""

Expand Down Expand Up @@ -590,8 +601,26 @@ class Settings extends Component<iSettingsProps,iSettingsState> {
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 (
<Container fluid className="background">
<Row className="title">
Expand All @@ -601,9 +630,14 @@ class Settings extends Component<iSettingsProps,iSettingsState> {
<Row xs="2" className="parent">
<Col xl="6">
<ListGroup className="heading-text">
<ListGroupItem>
{/* <ListGroupItem>
<img src={this.state.profile_pic} alt={'profile pic'} className="img-responsive"></img>
</ListGroupItem> */}
<ListGroupItem className="img-item">
<ProfilePicModal {...profilePicProps} />

</ListGroupItem>

<ListGroupItem>
<span className="heading-item">{this.state.first_name + " " + this.state.last_name}</span>
<a href="/#" className="modal-link" onClick={this.toggleNameModal}>
Expand Down Expand Up @@ -873,4 +907,11 @@ class Settings extends Component<iSettingsProps,iSettingsState> {

}

export default Settings;
function mapStateToProps(state: any) {
return {
tutorData: state.tutorData,
clientData: state.clientData
}
}

export default connect(mapStateToProps)(Settings);
2 changes: 0 additions & 2 deletions client/src/containers/DashboardPage/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 14 additions & 1 deletion client/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = {
Expand Down
40 changes: 40 additions & 0 deletions server/routes/api/tutors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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);

Expand Down Expand Up @@ -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 = {}

Expand Down Expand Up @@ -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 }
)
Expand Down
22 changes: 20 additions & 2 deletions server/routes/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {}

Expand Down