diff --git a/package.json b/package.json index a8ae1ad..e45497e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "firebase-admin": "^6.0.0", "flamelink": "^0.19.2", "google-maps-react": "^2.0.2", + "moment": "^2.22.2", "react": "^16.5.1", "react-dom": "^16.5.1", "react-quiz-component": "0.2.0", diff --git a/src/components/QuizGame.js b/src/components/QuizGame.js index bbfe640..1339978 100644 --- a/src/components/QuizGame.js +++ b/src/components/QuizGame.js @@ -3,21 +3,9 @@ import Grid from '@material-ui/core/Grid'; import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import Quiz from 'react-quiz-component'; - -/** - * Shuffles a given array. - * @param {*} array The array passed in. - */ -function shuffleArray(array) { - var j, x, i; - for (i = array.length - 1; i > 0; i--) { - j = Math.floor(Math.random() * (i + 1)); - x = array[i]; - array[i] = array[j]; - array[j] = x; - } - return array; -} +import { Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import RefreshIcon from '@material-ui/icons/Refresh'; // Style for the tabs. const styles = theme => ({ @@ -25,13 +13,45 @@ const styles = theme => ({ flexGrow: 1, backgroundColor: theme.palette.background.paper, }, + button: { + margin: theme.spacing.unit, + }, + rightIcon: { + marginLeft: theme.spacing.unit, + }, }); class QuizGame extends React.Component { + /** + * Shuffles a given array. + * @param {*} array The array passed in. + */ + shuffleArray = array => { + let shuffled = array; + + var j, x, i; + + for (i = shuffled.length - 1; i > 0; i--) { + j = Math.floor(Math.random() * (i + 1)); + x = shuffled[i]; + shuffled[i] = shuffled[j]; + shuffled[j] = x; + } + + return shuffled; + } + + reset = () => { + this.setState({ + difficulty: this.pickDifficulty(this.props.difficulty), + key: Math.random() + }); + } + easy = { "quizTitle": "Trail Cam Quiz: Easy", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -87,12 +107,12 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } medium = { "quizTitle": "Trail Cam Quiz: Medium", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -148,12 +168,12 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } hard = { "quizTitle": "Trail Cam Quiz: Hard", - "questions": shuffleArray([ + "questions": [ { "question": What animal is this?

, "questionType": "text", @@ -209,20 +229,29 @@ class QuizGame extends React.Component { ], "correctAnswer": "3" }, - ]) + ] } + /** + * This function returns the + * quiz data based on the difficulty + * level passed into it. + * @param {*} difficulty The difficulty setting passed in. + */ pickDifficulty = difficulty => { let level switch (difficulty) { case 'Easy': + this.easy.questions = this.shuffleArray(this.easy.questions) level = this.easy break case 'Medium': + this.medium.questions = this.shuffleArray(this.medium.questions) level = this.medium break case 'Hard': + this.hard.questions = this.shuffleArray(this.hard.questions) level = this.hard break default: @@ -234,8 +263,8 @@ class QuizGame extends React.Component { // The state of the component. state = { - //difficulty: pickDifficulty(this.props.difficulty) - difficulty: this.pickDifficulty(this.props.difficulty) + difficulty: this.pickDifficulty(this.props.difficulty), + key: Math.random() } // Renders the quiz component. @@ -245,9 +274,17 @@ class QuizGame extends React.Component { return ( // Tabs
- - - + + + + + + + +
); } diff --git a/src/components/ReportForm.js b/src/components/ReportForm.js index 9815843..7d50473 100644 --- a/src/components/ReportForm.js +++ b/src/components/ReportForm.js @@ -4,6 +4,10 @@ import Grid from '@material-ui/core/Grid'; import { withStyles } from '@material-ui/core/styles'; import MenuItem from '@material-ui/core/MenuItem'; import TextField from '@material-ui/core/TextField'; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; import Button from '@material-ui/core/Button'; import firebase from '../firebase.js'; import GoogleMap from '../components/ReportMap'; @@ -33,6 +37,17 @@ const styles = theme => ({ dense: { marginTop: 30, }, + close: { + padding: theme.spacing.unit / 2, + }, + icon: { + fontSize: 20, + marginRight: theme.spacing.unit, + }, + message: { + display: 'flex', + alignItems: 'center', + }, menu: { width: 200, }, @@ -220,7 +235,8 @@ class ReportForm extends React.Component { confidence: '1', desc: '', lat: '', - lng: '' + lng: '', + open: false }; /** @@ -233,6 +249,17 @@ class ReportForm extends React.Component { }); }; + /** + * Handles closing the toast. + */ + handleClose = (event, reason) => { + if (reason === 'clickaway') { + return; + } + + this.setState({ open: false }); + }; + /* * Get the coordinates * @@ -274,7 +301,8 @@ class ReportForm extends React.Component { confidence: '1', desc: '', lat: '', - lng: '' + lng: '', + open: true }); }; @@ -440,6 +468,30 @@ class ReportForm extends React.Component { + Report received.} + action={[ + + + , + ]} + /> ); } diff --git a/src/components/SightingMap.js b/src/components/SightingMap.js index e2c3e89..5535b68 100644 --- a/src/components/SightingMap.js +++ b/src/components/SightingMap.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import { Map, InfoWindow, Marker, GoogleApiWrapper } from 'google-maps-react'; +import moment from 'moment' import Typography from '@material-ui/core/Typography'; import firebase from '../firebase.js'; @@ -12,6 +13,128 @@ const mapStyles = { height: '100%' } +/** + * Types of sightings. Label is what is + * viewed in the application, value is + * what is stored in the database. + */ +const sightingTypes = [ + { + value: 'visual', + label: 'Visual', + }, + { + value: 'roadkill', + label: 'Roadkill', + }, + { + value: 'trapped', + label: 'Trapped', + }, + { + value: 'viewed_tracks', + label: 'Viewed Tracks', + }, + { + value: 'photo', + label: 'Photo', + }, + { + value: 'other', + label: 'Other', + }, +]; + +/** + * Types of sightings. Label is what is + * viewed in the application, value is + * what is stored in the database. +*/ +const timeTypes = [ + { + value: 'unknown', + label: 'Unknown', + }, + { + value: 'morning', + label: 'Morning', + }, + { + value: 'midday', + label: 'Midday', + }, + { + value: 'evening', + label: 'Evening', + }, + { + value: 'night', + label: 'Night', + }, +]; + +/** + * Levels of confidence. Label is what is + * viewed in the application, value is + * what is stored in the database. +*/ +const confidenceLevels = [ + { + value: '1', + label: '1 - Strongly unconfident', + }, + { + value: '2', + label: '2 - Unconfident', + }, + { + value: '3', + label: '3 - Somewhat confident', + }, + { + value: '4', + label: '4 - Confident', + }, + { + value: '5', + label: '5 - Very confident', + }, +]; + +/** + * Gets formatted confidence value. + */ +function getConfidence(item) { + for (var i = 0; i < confidenceLevels.length; i++) { + if (confidenceLevels[i].value === item) { + return confidenceLevels[i].label; + } + } + +} + +/** + * Gets formatted time value. + */ +function getTime(item) { + for (var i = 0; i < timeTypes.length; i++) { + if (timeTypes[i].value === item) { + return timeTypes[i].label; + } + } +} + +/** + * Gets formatted type value. + */ +function getType(item) { + for (var i = 0; i < sightingTypes.length; i++) { + if (sightingTypes[i].value === item) { + return sightingTypes[i].label; + } + } +} + export class MapContainer extends Component { // Get the user's location using Google's geolocation @@ -19,22 +142,22 @@ export class MapContainer extends Component { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((position) => { this.setState({ - myLatLng: { - lat: position.coords.latitude, - lng: position.coords.longitude - } + myLatLng: { + lat: position.coords.latitude, + lng: position.coords.longitude } + } ); }) } else { // If browser doesn't support geolocation or if user does not allow it, // center map on Grand Rapids, Michigan this.setState({ - myLatLng: { - lat: 42.9634, - lng: 85.6681 - } + myLatLng: { + lat: 42.9634, + lng: 85.6681 } + } ); } } @@ -85,6 +208,10 @@ export class MapContainer extends Component { } } + formatDate = date => { + return (moment(date, "YYYY-MM").format("MMMM YYYY").toString()) + } + // Set the state of the component to contain user coordinates and initial // marker and info window information state = { @@ -101,55 +228,55 @@ export class MapContainer extends Component { render() { return ( // Render the Google Map, Marker, and InfoWindow components -
+
+ style={mapStyles} + google={this.props.google} + initialCenter={this.state.myLatLng} + center={this.state.myLatLng} + defaultZoom={15} + onClick={this.onMapClick} > - - { this.state.sightings.map((sighting) => { + {this.state.sightings.map((sighting) => { return ( Confidence: {getConfidence(sighting.confidence)}} + date = {Date: {this.formatDate(sighting.date)}} + time = {Time: {getTime(sighting.time)}} + description = {Description: {sighting.desc}} /> ) })} + marker={this.state.activeMarker} + visible={this.state.showingInfoWindow} > - - { this.state.selectedPlace.type } + + {this.state.selectedPlace.type} - - { this.state.selectedPlace.confidence } + + {this.state.selectedPlace.confidence} - - { this.state.selectedPlace.date } + + {this.state.selectedPlace.date} - - { this.state.selectedPlace.time } + + {this.state.selectedPlace.time} - - { this.state.selectedPlace.description } + + {this.state.selectedPlace.description}