import React, { Component } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";
import { FirebaseContext } from "../../../components/Firebase/index";
import AppBar from "../../../components/AppBar";
import LabeledInfo from "../../../components/MyTeachersComponents/LabeledInfo";
import TransitionTimeIconImage from "../../../assets/images/TransitionTimeIconImage.svg";
import EngagementIconImage from "../../../assets/images/EngagementIconImage.svg";
import SequentialIconImage from "../../../assets/images/SequentialIconImage.svg";
import ListeningIconImage from "../../../assets/images/ListeningIconImage.svg";
import MathIconImage from "../../../assets/images/MathIconImage.svg";
import InstructionIconImage from "../../../assets/images/InstructionIconImage.svg";
import ClassroomClimateIconImage from "../../../assets/images/ClassroomClimateIconImage.svg";
import AssocCoopIconImage from "../../../assets/images/AssocCoopIconImage.svg";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import Fab from "@material-ui/core/Fab";
import ChevronLeftRoundedIcon from "@material-ui/icons/ChevronLeftRounded";
import DeleteForeverIcon from "@material-ui/icons/DeleteForever";
import EditOutlinedIcon from "@material-ui/icons/EditOutlined";
import Grid from "@material-ui/core/Grid";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import TextField from "@material-ui/core/TextField";
const styles = {
root: {
flexGrow: 1,
width: "100%",
maxHeight: "100%"
},
container: {
// border: '2px solid #000000',
margin: "2% 10% 0 10%",
display: "flex",
flexDirection: "column",
flexGrow: 1,
alignItems: "flex-start",
maxHeight: "50%"
},
button: {
color: "#333333",
borderRadius: 3,
textTransform: "none"
},
teacherHeader: {
// border: '2px solid #DC143C',
flexGrow: 1,
display: "flex",
justifyContent: "space-between",
fontSize: "2em",
marginTop: "1em",
minWidth: "40%",
maxWidth: "50%"
},
contentContainer: {
// border: '2px solid #F700FF',
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "100%"
},
teacherCard: {
// border: '2px solid #7FFF00',
display: "flex",
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "stretch",
flexGrow: 1,
marginTop: "1em",
width: "50%",
fontSize: "1.5em"
},
magicEightCard: {
// border: '2px solid #00FFFF',
padding: "0px",
width: "50%",
margin: "0",
flexGrow: 1,
display: "grid",
gridTemplateColumns: "25% 25% 25% 25%",
gridTemplateRows: "50% 50%"
},
actionButton: {
marginLeft: "1em",
"&:hover": {
boxShadow: "0 2px 5px 5px rgba(0, 0, 0, 0.2)"
}
},
magicEightItem: {
margin: "3% 3% 15% 3%",
flexBasis: "22%",
listStyleType: "none",
textAlign: "center"
},
magicEightButton: {
marginBottom: "15%",
backgroundColor: "#FFFFFF",
border: "0 none #FFFFFF",
borderRadius: "10%",
padding: 0,
width: "80%",
boxShadow: "none",
opacity: 0.95,
"&:hover": {
backgroundColor: "#8B8B8B",
opacity: 1
}
},
img: {
maxHeight: "100px",
margin: "5%"
},
deleteModalButtonContainer: {
display: "flex",
justifyContent: "space-around"
},
deleteModalButton: {
border: "2px solid #000000",
textTransform: "none",
fontSize: "1em"
},
// Minor Breakpoint -> Shrinking desktop window
"@media only screen and (max-width:1270px)": {
container: {
margin: "2% 7% 0 7%"
}
},
// Minor Breakpoint -> Shrinking desktop window
"@media only screen and (max-width:1145px)": {
container: {
margin: "2% 5% 0 5%"
},
teacherHeader: {
minWidth: "50%"
}
},
// iPad Pro 12.9" Portrait
"@media only screen and (max-width:1024px) and (orientation:portrait)": {
container: {
margin: "2% 5% 0 5%",
fontSize: "1.5em"
},
teacherHeader: {
minWidth: "60%",
borderBottom: "1px solid #B3D8E6"
},
contentContainer: {
flexDirection: "column",
justifyContent: "flex-start"
},
teacherCard: {
flexFlow: "row wrap",
justifyContent: "space-between",
alignItems: "flex-start",
width: "100%",
fontSize: "1.3em",
borderBottom: "1px solid #B3D8E6"
},
magicEightCard: {
width: "100%",
margin: "1em 0 0 0"
}
},
// iPad Pro 10.5" Portrait
"@media only screen and (max-width:834px) and (orientation: portrait)": {
container: {
margin: "2% 5% 0 5%",
fontSize: "1em"
},
teacherHeader: {
minWidth: "60%",
borderBottom: "1px solid #B3D8E6"
},
contentContainer: {
flexDirection: "column",
justifyContent: "flex-start"
},
teacherCard: {
flexFlow: "row wrap",
justifyContent: "space-between",
alignItems: "flex-start",
width: "100%",
fontSize: "1.3em",
borderBottom: "1px solid #B3D8E6"
},
magicEightCard: {
width: "100%",
margin: "2em 0 0 0"
}
},
// iPad-Mini Portrait
"@media only screen and (max-width:768px) and (orientation:portrait)": {
container: {
margin: "2% 5% 0 5%"
},
teacherHeader: {
minWidth: "60%",
borderBottom: "1px solid #B3D8E6"
},
contentContainer: {
flexDirection: "column",
justifyContent: "flex-start"
},
teacherCard: {
flexFlow: "row wrap",
justifyContent: "space-between",
alignItems: "flex-start",
width: "100%",
borderBottom: "1px solid #B3D8E6"
},
magicEightCard: {
width: "100%",
margin: 0
}
},
// iPad-Mini Landscape
"@media only screen and (max-width:1024px) and (orientation:landscape)": {
container: {
margin: "2% 7% 0 7%"
},
teacherHeader: {
minWidth: "50%",
maxWidth: "60%"
},
teacherCard: {
width: "40%",
fontSize: "1.3em"
},
magicEightCard: {
width: "60%"
}
}
};
const sortedSvg = [
TransitionTimeIconImage,
ClassroomClimateIconImage,
ListeningIconImage,
InstructionIconImage,
MathIconImage,
EngagementIconImage,
SequentialIconImage,
AssocCoopIconImage
];
/**
* page with details about a teacher
* @class TeacherDetailPage
*/
class TeacherDetailPage extends Component {
/**
* @param {Props} props
*/
constructor(props) {
super(props);
const id = this.props.match.params.teacherid;
this.initialState = {
teacherUID: id,
firstName: "...",
lastName: "...",
school: "...",
email: "...",
phone: "...",
notes: "...",
inputFirstName: "",
inputLastName: "",
inputSchool: "",
inputEmail: "",
inputPhone: "",
inputNotes: "",
fnErrorText: "",
lnErrorText: "",
schoolErrorText: "",
emailErrorText: "",
notesErrorText: "",
isEditing: false,
isDeleting: false,
editAlert: false,
alertText: "",
recentObs: [
null, // transition
null, // climate
null, // listening
null, // level
null, // math
null, // engagement
null, // sequential
null // AC
]
};
if (this.props.location.state === undefined) {
// Entered URL w/o navigating from 'My Teachers'
this.state = this.initialState;
} else {
// Came from 'My Teachers'
const {
id,
firstName,
lastName,
school,
email,
phone,
notes
} = this.props.location.state.teacher;
this.state = {
teacherUID: id,
firstName: firstName,
lastName: lastName,
school: school,
email: email,
phone: phone,
notes: notes,
inputFirstName: firstName,
inputLastName: lastName,
inputSchool: school,
inputEmail: email,
inputPhone: phone,
inputNotes: notes,
fnErrorText: "",
lnErrorText: "",
schoolErrorText: "",
emailErrorText: "",
notesErrorText: "",
isEditing: false,
isDeleting: false,
editAlert: false,
alertText: "",
recentObs: [
null, // transition
null, // climate
null, // listening
null, // level
null, // math
null, // engagement
null, // sequential
null // AC
]
};
}
this.componentDidMount = this.componentDidMount.bind(this);
}
/** lifecycle method invoked after component mounts */
componentDidMount() {
const firebase = this.context;
firebase
.getTeacherInfo(this.state.teacherUID)
.then(teacherInfo => {
this.setState({
firstName: teacherInfo.firstName,
lastName: teacherInfo.lastName,
school: teacherInfo.school,
email: teacherInfo.email,
phone: teacherInfo.phone,
notes: teacherInfo.notes,
inputFirstName: teacherInfo.firstName,
inputLastName: teacherInfo.lastName,
inputSchool: teacherInfo.school,
inputEmail: teacherInfo.email,
inputPhone: teacherInfo.phone,
inputNotes: teacherInfo.notes
}); // Automatically forces a re-render
firebase
.getRecentObservations(this.state.teacherUID)
.then(recentObs =>
this.setState({
recentObs: recentObs
})
)
.catch(error =>
console.error("Error occurred getting recent observations: ", error)
);
})
.catch(error => {
console.error("Error fetching Teacher's Info: ", error);
});
}
handleCloseModal = () => {
const { firstName, lastName, school, email, phone, notes } = this.state;
this.setState({
inputFirstName: firstName,
inputLastName: lastName,
inputSchool: school,
inputEmail: email,
inputPhone: phone,
inputNotes: notes,
fnErrorText: "",
lnErrorText: "",
schoolErrorText: "",
emailErrorText: "",
phoneErrorText: "",
notesErrorText: "",
isEditing: false,
isDeleting: false,
alertText: ""
});
};
/**
* @param {event} e
*/
handleEditText = e => {
const type = e.target.name;
const val = e.target.value;
this.setState(
{
[type]: val
},
() => this.validateInputText(type, val)
);
};
/**
* validates user-entered text
* @param {type} type
* @param {value} val
*/
validateInputText = (type, val) => {
switch (type) {
case "inputFirstName":
if (!/^[a-zA-Z ]{2,30}$/.test(val)) {
this.setState({ fnErrorText: "Invalid first name." });
} else {
this.setState({ fnErrorText: "" });
}
break;
case "inputLastName":
if (!/^[a-zA-Z ]{2,30}$/.test(val)) {
this.setState({ lnErrorText: "Invalid last name." });
} else {
this.setState({ lnErrorText: "" });
}
break;
case "inputEmail":
if (!/^\S+@\S+$/.test(val)) {
this.setState({ emailErrorText: "Invalid email address." });
} else {
this.setState({ emailErrorText: "" });
}
break;
case "inputPhone":
if (val === "") {
this.setState({ phoneErrorText: "" });
} else if (!/^\d{3}?-\d{3}-\d{4}$/.test(val)) {
this.setState({
phoneErrorText: "Invalid number or format (use ###-###-####)."
});
} else {
this.setState({ phoneErrorText: "" });
}
break;
case "inputSchool":
if (!/^[a-zA-Z ]{2,50}$/.test(val)) {
this.setState({
schoolErrorText: "Invalid school (max 50 characters)."
});
} else {
this.setState({ schoolErrorText: "" });
}
break;
case "inputNotes":
if (val.length > 250) {
this.setState({ notesErrorText: "Max 250 characters." });
} else {
this.setState({ notesErrorText: "" });
}
break;
default:
break;
}
};
handleEditConfirm = () => {
const {
teacherUID,
inputFirstName,
inputLastName,
inputSchool,
inputEmail,
inputNotes,
inputPhone,
fnErrorText,
lnErrorText,
emailErrorText,
schoolErrorText,
notesErrorText,
phoneErrorText
} = this.state;
this.validateInputText("inputFirstName", inputFirstName);
this.validateInputText("inputLastName", inputLastName);
this.validateInputText("inputEmail", inputEmail);
this.validateInputText("inputSchool", inputSchool);
if (
// any inputs cause an error or required are missing
!!fnErrorText ||
!!lnErrorText ||
!!emailErrorText ||
!!schoolErrorText ||
!!notesErrorText ||
!!phoneErrorText ||
!inputFirstName ||
!inputLastName ||
!inputEmail ||
!inputSchool
) {
return null;
} else {
// fields are validated
const firebase = this.context;
if (
firebase.setTeacherInfo(teacherUID, {
firstName: inputFirstName,
lastName: inputLastName,
school: inputSchool,
email: inputEmail,
phone: inputPhone,
notes: inputNotes
})
) {
// Edit successfully written
this.setState(
{
firstName: inputFirstName,
lastName: inputLastName,
school: inputSchool,
email: inputEmail,
phone: inputPhone,
notes: inputNotes,
isEditing: false,
fnErrorText: "",
lnErrorText: "",
schoolErrorText: "",
emailErrorText: "",
notesErrorText: ""
},
() => this.handleEditAlert(true)
);
} else {
this.handleCloseModal();
this.handleEditAlert(false);
}
}
};
/**
* @param {boolean} successful
*/
handleEditAlert = successful => {
if (successful) {
this.setState(
{
editAlert: true,
alertText: "Edit Successfully Written!"
},
() => setTimeout(() => this.setState({ editAlert: false }), 1500)
);
} else {
this.setState(
{
editAlert: true,
alertText:
"Something went wrong... try refreshing your page or logging out and back in."
},
() => setTimeout(() => this.setState({ editAlert: false }), 3000)
);
}
};
handleDeleteConfirm = () => {
const firebase = this.context;
firebase
.removePartner(this.state.teacherUID)
.then(() => {
this.setState(this.initialState, () => {
if (this.props.location.state !== undefined) {
// came from MyTeachers
this.props.history.goBack();
} else {
this.props.history.replace("/MyTeachers");
}
});
})
.catch(() =>
this.setState(
{
editAlert: true,
alertText:
"Something went wrong removing this teacher... " +
"try refreshing your page or logging out and back in."
},
() => setTimeout(() => this.setState({ editAlert: false }), 3000)
)
);
};
/**
* render function
* @return {ReactElement}
*/
render() {
const { classes } = this.props;
const {
firstName,
lastName,
school,
email,
phone,
notes,
isEditing,
isDeleting,
editAlert,
alertText,
fnErrorText,
lnErrorText,
schoolErrorText,
emailErrorText,
phoneErrorText,
notesErrorText,
recentObs
} = this.state;
return (
<div className={classes.root}>
<FirebaseContext.Consumer>
{firebase => <AppBar firebase={firebase} />}
</FirebaseContext.Consumer>
<div className={classes.container}>
<Button
variant="contained"
size="medium"
className={classes.button}
onClick={() => {
if (this.props.location.state !== undefined) {
// came from MyTeachers
this.props.history.goBack();
} else {
this.props.history.replace("/MyTeachers");
}
}}
>
<ChevronLeftRoundedIcon />
<b>My Teachers</b>
</Button>
<div className={classes.teacherHeader}>
<span>
<b>
{firstName} {lastName}
</b>
<br />
Teacher
</span>
{this.state.teacherUID === "rJxNhJmzjRZP7xg29Ko6" ? null : ( // Prevent deleting and editing the Practice Teacher!
<div>
<Fab
aria-label="Edit"
name="Edit"
size="small"
onClick={() => this.setState({ isEditing: true })}
className={classes.actionButton}
style={{ backgroundColor: "#F9FE49" }}
>
<EditOutlinedIcon style={{ color: "#555555" }} />
</Fab>
<Fab
aria-label="Delete"
onClick={() => this.setState({ isDeleting: true })}
className={classes.actionButton}
size="small"
style={{ backgroundColor: "#FF3836" }}
>
<DeleteForeverIcon style={{ color: "#C9C9C9" }} />
</Fab>
</div>
)}
</div>
<Grid
container
direction="row"
justify="space-between"
alignItems="stretch"
className={classes.contentContainer}
>
<div className={classes.teacherCard}>
<div
style={{
display: "flex",
flexDirection: "row",
minWidth: "45%"
}}
>
<LabeledInfo label="First Name" field={firstName} />
<LabeledInfo label="Last Name" field={lastName} />
</div>
<LabeledInfo label="School" field={school} />
<LabeledInfo label="Email" field={email} />
<LabeledInfo label="Phone" field={phone} />
<LabeledInfo label="Notes" field={notes} />
</div>
<ol className={classes.magicEightCard}>
{sortedSvg.map((item, key) =>
recentObs !== undefined && recentObs[key] !== null ? (
<li key={key} className={classes.magicEightItem}>
<Button
variant="contained"
className={classes.magicEightButton}
style={{
border: "1px solid #000000",
backgroundColor: "#000000"
}}
>
<img
src={item}
alt="Magic Eight"
className={classes.img}
/>
</Button>
<p>
Last Observed:
<br />
{recentObs[key]}
</p>
</li>
) : (
<li key={key} className={classes.magicEightItem}>
<Button
disabled
variant="contained"
className={classes.magicEightButton}
style={{ backgroundColor: "#FFFFFF", opacity: 0.8 }}
>
<img
src={item}
alt="Magic Eight"
className={classes.img}
/>
</Button>
<p>
Not Yet
<br />
Observed
<br />
<br />
</p>
</li>
)
)}
</ol>
</Grid>
<Dialog
open={isDeleting}
onClose={this.handleCloseModal}
aria-labelledby="delete-teacher-modal"
aria-describedby="prompts a coach to confirm deleting a teacher from My Teachers"
>
<DialogTitle
id="delete-teacher-title"
style={{ textAlign: "center" }}
>
Are you sure you want to remove
<b style={{ textDecoration: "underline", color: "#2196F3" }}>
{firstName} {lastName}
</b>{" "}
from My Teachers?
</DialogTitle>
<DialogActions className={classes.deleteModalButtonContainer}>
<Button
onClick={() => this.setState({ isDeleting: false })}
className={classes.deleteModalButton}
autoFocus
style={{ borderColor: "#2196F3" }}
>
No,
<b style={{ color: "#2196F3", padding: "0 0.3em 0 0.3em" }}>
KEEP
</b>
{firstName} {lastName}
</Button>
<Button
onClick={this.handleDeleteConfirm}
className={classes.deleteModalButton}
style={{ borderColor: "#F1231C" }}
>
Yes,
<b style={{ color: "#F1231C", padding: "0 0.3em 0 0.3em" }}>
DELETE
</b>
{firstName} {lastName}
</Button>
</DialogActions>
</Dialog>
<Dialog
open={isEditing}
onClose={this.handleCloseModal}
aria-labelledby="edit-teacher-title"
>
<DialogTitle id="edit-teacher-title">
Edit {firstName} {lastName}'s Info
</DialogTitle>
<DialogContent>
<DialogContentText>
Make edits to the form below and confirm to update this
teacher's information.
</DialogContentText>
<TextField
autoFocus
defaultValue={firstName}
onChange={this.handleEditText}
margin="dense"
id="first-name"
name="inputFirstName"
label="First Name"
type="text"
helperText={fnErrorText}
error={!!fnErrorText} // false when empty "", true o/w
fullWidth
/>
<TextField
defaultValue={lastName}
onChange={this.handleEditText}
margin="dense"
id="last-name"
name="inputLastName"
label="Last Name"
type="text"
helperText={lnErrorText}
error={!!lnErrorText}
fullWidth
/>
<TextField
defaultValue={school}
onChange={this.handleEditText}
margin="dense"
id="school"
name="inputSchool"
label="School"
type="text"
helperText={schoolErrorText}
error={!!schoolErrorText}
fullWidth
/>
<TextField
defaultValue={email}
onChange={this.handleEditText}
margin="dense"
id="email"
name="inputEmail"
label="Email"
type="email"
helperText={emailErrorText}
error={!!emailErrorText}
fullWidth
/>
<TextField
autoFocus
defaultValue={phone}
onChange={this.handleEditText}
margin="dense"
id="phone"
name="inputPhone"
label="Phone"
type="text"
helperText={phoneErrorText}
error={!!phoneErrorText} // false when empty "", true o/w
fullWidth
/>
<TextField
defaultValue={notes}
onChange={this.handleEditText}
margin="dense"
id="notes"
name="inputNotes"
label="Notes"
helperText={notesErrorText}
error={!!notesErrorText}
multiline
rows={10}
rowsMax={10}
fullWidth
/>
</DialogContent>
<DialogActions>
<Button
onClick={this.handleCloseModal}
style={{ color: "#F1231C" }}
>
Cancel
</Button>
<Button
onClick={this.handleEditConfirm}
style={{ color: "#2196F3" }}
>
Confirm Edits
</Button>
</DialogActions>
</Dialog>
<Dialog
open={editAlert}
onClose={() => this.setState({ editAlert: false, alertText: "" })}
aria-labelledby="edit-alert-label"
aria-describedby="edit-alert-description"
>
<DialogTitle id="edit-alert-title">{alertText}</DialogTitle>
</Dialog>
</div>
</div>
);
}
}
TeacherDetailPage.propTypes = {
classes: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
TeacherDetailPage.contextType = FirebaseContext;
export default withStyles(styles)(withRouter(TeacherDetailPage));