<repo_name>PIUisUndefined/tripteaze<file_sep>/database-mongo/index.js
var mongoose = require('mongoose');

let uri;


if (!process.env.MONGODB_URI) {
  config = require('../config.js');
  uri = config.mongo;
} else {
  console.log('PROCESS PICKED UP', process.env.MONGODB_URI)
  uri = process.env.MONGODB_URI;
}
//URI is stored either on heroku or local config file
let Schema = mongoose.Schema;
mongoose.connect(uri);

var db = mongoose.connection;

db.on('error', function() {
  console.log(uri);
  console.log('mongoose connection error');
});

db.once('open', function() {
  console.log('mongoose connected successfully');
});

function toLower (v) {
  return v.toLowerCase();
}

var userSchema = Schema({
  id: Schema.Types.ObjectId,
  name: {type: String, set: toLower, index: true, required: [true, "can't be blank"]},
  password: <PASSWORD>
});

var tripSchema = Schema({
  id: Schema.Types.ObjectId,
  city: String,
  tripFromDate: Date,
  tripToDate: Date,
  isPublic: {type: Boolean, default: false},
  isArchived: {type: Boolean, default: false},
  //need to make sure each trip has a reference user
  user: {type: Schema.Types.ObjectId, ref: 'User'}

});

var restaurantSchema = Schema({
  id: {type: Number, index: true},
  name: String,
  url: String,
  address: String,
  zip: Number,
  logo: String,
  //latitude and longitude coordinates are placed in 'location' property
  location: [{type: Number}],
  price: Number,
  //need to make sure each restaurant or event has a reference trip
  trip: {type: Schema.Types.ObjectId, ref: 'Trip'}
});


var eventSchema = Schema({
  id: {type: Number, index: true},
  name: String,
  description: String,
  url: String,
  start_time: String,
  end_time: String,
  is_free: Boolean,
  organizer_id: Number,
  venue_id: Number,
  category_id: Number,
  logo: String,
  //need to make sure each restaurant or event has a reference trip
  trip: {type: Schema.Types.ObjectId, ref: 'Trip'}
});

var User = mongoose.model('User', userSchema);
var Trip = mongoose.model('Trip', tripSchema);
var Restaurant = mongoose.model('Restaurant', restaurantSchema);
var Event = mongoose.model('Event', eventSchema);

let addNewTrip = (username, city, fromDate, toDate, callback) => {
  User.findOne({name: username}, function (err, user) {
    if(err) {
      callback(err);
    }
    Trip.create({
      id: new mongoose.Types.ObjectId(),
      city: city,
      user: user.id,
      // Dates need to be in YYYY-MM-DD format
      tripFromDate: fromDate,
      tripToDate: toDate
    }, (err, data) => {
      if(err) {
        callback(err);
      } else {
        callback(null, data);
      }
    });
  });
};

let addRestaurantToTrip = (food, username, city, callback) => {
  //first find corresponding user
  User.findOne({name: username}, function (err, user) {
    if(err) {
      console.log('error: ', err);
      callback(err);
    } else {
      
      Trip.findOne({user: user.id, city: city}, function (err, trip) {
        if(err) {
          console.log('error', err);
          callback(err);
        } else {

          Restaurant.findOneAndUpdate({ id: food.restaurant.id},
            {$set: {
              id: food.restaurant.id,
              name: food.restaurant.name,
              url: food.restaurant.url,
              logo: food.restaurant.featured_image,
              address: food.restaurant.location.address,
              zip: food.restaurant.location.zipcode,
              location: [food.restaurant.location.latitude, food.restaurant.location.longitude],
              price: food.restaurant.price_range,
              trip: trip.id
              }
            }, {upsert: true}, function(err) {
              if(err) {
                console.log('error: ', err);
                callback(err);
              } else {
                callback();
              }
            }
          );
        }
        //then add restaurant to database based on trip ID
      });
    }
    //then find corresponding trip based on city for selected user
  });
};

let addEventToTrip = (event, username, city, callback) => {
  //first find corresponding user
  User.findOne({name: username}, function (err, user) {
    if(err) {
      console.log('error: ', err);
      callback(err);
    }
    //then find corresponding trip based on city for selected user
    Trip.findOne({user: user.id, city: city}, function (err, trip) {
      if(err) {
        console.log('error', err);
        callback(err);
      }
      //then add event to database based on trip ID
      //need to look at eventbrite API for structure
      Event.findOneAndUpdate({id: event.id},
        {$set: {
          name: event.name.text,
          description: event.description.text,
          id: event.id,
          url: event.url,
          start_time: event.start.local,
          end_time: event.end.local,
          is_free: event.is_free,
          organizer_id: event.organizer_id,
          venue_id: event.venue_id,
          category_id: event.category_id,
          logo: event.logo.url,
          trip: trip.id
          }
        }, {upsert: true}, function(err) {
          if(err) {
            console.log('error: ', err);
            callback(err);
          } else {
            callback();
          }
        }
      );
    });
  });
};

//for signup page-takes in username and password and adds user info to database
//need to make sure usernames are unique in db
let addNewUser = (name, password) => {
  User.findOneAndUpdate({name: name},
    {$set: {
      id: new mongoose.Types.ObjectId(),
      name: name,
      password: <PASSWORD>
      }
    }, {upsert: true},
    function(err) {
      if(err) {
        console.log('error: ', err);
      }
    }
  );
};

// checks if username already exists in the database and
// returns that user
let userExists = (username, cb) => {
  // checks database based on input username
  User.find({
    name: username
  }, (err, existingUser) => {
    if (err) {
      console.error('error in userExists: ', err);
    } else {
      // callback on the existing user if it exists
      cb(existingUser);
    }
  })
};

//for login page-take in username and retrieve password from db
//on server side, bcrypt will be used to compare user input password to stored db password
//if they match user will be logged in, otherwise error message
let retrieveUserPassword = (username, callback) => {
  User.find({name: username}, function(err, user) {
    // If the user exists in the database
    if (user.length > 0) {
      // Then run the callback on that user's password
      callback(null, user[0].password);
    } else {
      // Should probably send an alert or something...
      console.log('user does not exist');
      callback('user does not exist');
    }
  });
};


//for user page-display all existing trips for user after being logged in
let showUserTrips = (username, callback) => {
  //first find corresponding user
  User.findOne({name: username}, function (err, user) {
    if(err || user === null) {
      console.log('error: ', err);
      callback(err);

    } else {
      //then find all trips for selected user
      Trip.find({user: user.id}, function (err, trips) {
        if(err) {
          callback(err, null);
        } else {
          callback(null, trips);
        }
      });
    }
  });
};

let showTripEvents = (username, city, callback) => {
//first find corresponding user
  User.findOne({name: username}, function (err, user) {
    if(err || user === null) {
      console.log('error: ', err);
      callback(err);
    } else {
      //then find trip based on selected user and city
      Trip.findOne({user: user.id, city: city}, function (err, trip) {
        if(err || trip === null) {
          console.log('error', err);
          callback(err);
        } else {
          if(err) {
            callback(err, null);
          } else {
            getTripEvents(trip.id, callback);
          }
        }
      });
    }
  });
};

let showTripRestaurants = (username, city, callback) => {
  //first find corresponding user
  User.findOne({ name: username }, function (err, user) {
    if (err || user === null) {
      console.log('error: ', err);
      callback(err);
    } else {
      //then find trip based on selected user and city
      Trip.findOne({ user: user.id, city: city }, function (err, trip) {
        if (err || trip === null) {
          console.log('error', err);
          callback(err);
        } else {
          if (err) {
            callback(err, null);
          } else {
            getTripRestaurants(trip.id, callback);
          }
        }
      });
    }
  });
};


//allows user to update whether trip is public, archived, and/or if the trip dates changed
//assumes username and city are known to obtain corresponding trip and update
let modifyTripDetails = (makePublic, makeArchived, username, fromDate, toDate, city, callback) => {
  //first find corresponding user
  User.findOne({name: username}, function (err, user) {
    if(err) {
      callback(err);
      console.log('error: ', err);
    }
    //then find corresponding trip based on city for selected user
    Trip.findOne({user: user.id, city: city}, function (err, trip) {
      if(err) {
        callback(err);
        console.log('error', err);
      }
      //makePublic = makePublic || trip.isPublic;
      makeArchived = makeArchived || trip.isArchived;
      newFromDate = fromDate || trip.tripFromDate;
      newToDate = toDate || trip.tripToDate;
      Trip.update({id: trip.id},
        {$set:
          {
            isPublic: makePublic,
            isArchived: makeArchived,
            tripFromDate: newFromDate,
            tripToDate: newToDate
          }
        }, function (err) {
          if (err) {
            callback(err);
            console.log('error: ', err);
          } else {
            callback();
          }
        }
      );
    });
  });
};

getTripEvents = (tripID, callback) => {
  Event.find({ trip: tripID }, function (err, events) {
    if (err) {
      callback(err, null);
    } else {
      callback(null, events);
    }
  });
}

getTripRestaurants = (tripID, callback) => {
  Restaurant.find({ trip: tripID }, function (err, eatin) {
    if (err) {
      callback(err, null);
    } else {
      callback(null, eatin);
    }
  });
}

//removal function assumes we know the ID of the restaurant, event,
//or trip that we are wanting to remove from the database
let remove = (modelType, ID, callback) => {
  if(modelType === 'restaurant') {
    Restaurant.remove( {id: ID}, function (err) {
      if(err) {
        console.log('error: ',err);
        callback(err)
      } else {
        callback()
      }
    });
  } else if (modelType === 'event') {
    Event.remove( {id: ID}, function (err) {
      if(err) {
        callback(err);
        console.log('error: ',err);
        callback(err)
      } else {
        callback()
      }
    });
  } else if (modelType === 'trip') {
    Trip.remove( {id: ID}, function (err) {
      if(err) {
        callback(err);
        console.log('error: ',err);
        callback(err)
      } else {
        callback()
      }
    });
  } else {
    console.log('must specify correct model type to remove');
    callback(err);
  }
};
//for home page-displays all existing public trips
let showAllPublicTrips = (callback) => {
  Trip.find({isPublic: true}, function(err, trips) {
    if(err) {
      callback(err, null);
    } else {
      callback(null, trips);
    }
  });
};



module.exports.addNewTrip = addNewTrip;
module.exports.addRestaurantToTrip = addRestaurantToTrip;
module.exports.addEventToTrip = addEventToTrip;
module.exports.addNewUser = addNewUser;
module.exports.retrieveUserPassword = retrieveUserPassword;
module.exports.showUserTrips = showUserTrips;
module.exports.modifyTripDetails = modifyTripDetails;
module.exports.remove = remove;
module.exports.showAllPublicTrips = showAllPublicTrips;
module.exports.userExists = userExists;
module.exports.showTripEvents = showTripEvents;
module.exports.showTripRestaurants = showTripRestaurants;
module.exports.getTripRestaurants = getTripRestaurants;
module.exports.getTripEvents = getTripEvents;


// {restaurant: { 
//   R: { res_id: 16608481 },
//   apikey: '<KEY>',
//   id: '16608481',
//   name: '<NAME>',
//   url: 'https://www.zomato.com/kingston-se-sa/old-woolstore-kingston-se?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1',
//   location: {
//     address: '11 Hansen St Kingston Sa, Kingston SE',
//     locality: 'Kingston SE',
//     city: 'Kingston SE',
//     city_id: 1814,
//     latitude: '-36.8287500000',
//     longitude: '139.8503820000',
//     zipcode: '5275',
//     country_id: 14,
//     locality_verbose: 'Kingston SE, Kingston SE'
//     },
//   switch_to_order_menu: 0,
//   cuisines: 'Others',
//   average_cost_for_two: 30,
//   price_range: 3,
//   currency: '$',
//   offers: [],
//   thumb: '',
//   user_rating: {
//     aggregate_rating: '2.8',
//     rating_text: 'Average',
//     rating_color: 'FFBA00',
//     votes: '4'
//     }
//   }
// }<file_sep>/react-client/src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';  //replace with axios
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

import reducer from './reducers/index.js';
import Home from './components/homePage.jsx';
import User from './components/userPage.jsx';
import Search from './components/searchPage.jsx';

import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom';

const Root = ({ store }) => (
  <Provider store={store}>
    <MuiThemeProvider theme={getMuiTheme(lightBaseTheme)}>
      <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path = "/trips" component={User} />
          <Route path="/plan" component={Search} />
        </Switch>
      </Router>
    </MuiThemeProvider>
  </Provider>
)

let store = createStore(reducer, applyMiddleware(thunk))

ReactDOM.render( <Root store={store} /> 
  , document.getElementById('app'));



<file_sep>/react-client/src/components/restaurants.jsx
import React from 'react';

import { GridList, GridTile } from 'material-ui/GridList';
import IconButton from 'material-ui/IconButton';
import AddBorder from 'material-ui/svg-icons/content/add-circle-outline';
import Snackbar from 'material-ui/Snackbar';

//these are search results for restaurants
const Eatin = (props) => {

  const styles = {
    root: {
      display: 'flex',
      flexWrap: 'wrap',
      justifyContent: 'space-around',
      // width: '50%'
    },
    gridList: {
      width: '100%',
      height: 800,
      overflowY: 'auto',
    },
    anchor: {
      color: 'white'
    }
  };

  if (props.restaurants.length !== 0) {
    return (
      <div style={styles.root}>
        <GridList
          cellHeight={180}
          style={styles.gridList}
        >
          {props.restaurants.map((food) => {
            if (food.restaurant.featured_image) {
              return (
                <GridTile
                  key={food.restaurant.id}
                  title= {<a style = {styles.anchor} href = {food.restaurant.url} target = "_blank" >{food.restaurant.name}</a>}
                  subtitle= {`Cost for Two $${food.restaurant.average_cost_for_two}`}
                  actionIcon={<IconButton onClick={() => props.addFoodToTrip(food, props.user, props.city)}><AddBorder color="white" /></IconButton>}
                >
                  <Snackbar open={props.foodSnackbar} message={'Restaurant has been added to your trip!'} autoHideDuration={3000} onRequestClose={props.onRequestClose}/>
                  <img src={food.restaurant.featured_image} alt='' />
                </GridTile>
              );
            } else {
              return(
                <GridTile
                  key={food.restaurant.id}
                  title={food.restaurant.name}
                  subtitle={`Cost for Two $${food.restaurant.average_cost_for_two}`}
                  actionIcon={<IconButton onClick={() => props.addFoodToTrip(food, props.user, props.city)}><AddBorder color="white" /></IconButton>}
                >
                  <Snackbar open={props.foodSnackbar} message={'Restaurant has been added to your trip!'} autoHideDuration={3000} onRequestClose={props.onRequestClose}/>
                  <img src='' />
                </GridTile>
              )
            }
          })}
        </GridList>
      </div>
    );
  } else {
    return null;
  }
}

export default Eatin;<file_sep>/react-client/src/reducers/index.js
const initialState = {
  loading: false,
  username: '',
  password: '',
  authenticated: false,
  currentError: '', //is never actually used rn
  publicTrips: [],
  userTrips: [],
  city: '',
  tripFromDate: '',
  tripToDate: '',
  isPublic: false,
  minToDate: {}, // so users cannot set a "to" date before the "from" date
  deleteSnackbar: false,
  publicSnackbar: false,
  privateSnackbar: false,
  eventQuery: '',
  eventResults: [],
  foodQuery: '',
  foodResults: [],
  eventSnackbar: false,
  foodSnackbar: false,
  activeTrip: {
    status: false,
    index: 0 //location of trip in 'userTrips'
  }
};
//you should probably look up combine reducers to make this easier to look at
const reducer = function (state = initialState, action) {  //if state is undefined, state becomes inital state

  switch (action.type) {
    case 'TOGGLE_LOADING' : 
      return Object.assign({}, state, {loading: !state.loading});
    case 'SHOW_USER_TRIPS' :
      return Object.assign({}, state, {userTrips: action.payload});
    case 'SHOW_PUBLIC_TRIPS' :
      return Object.assign({}, state, {publicTrips: action.payload});
    case 'RESET_USER_TRIPS' :
      return Object.assign({}, state, {userTrips: []});
    case 'ACTIVATE_EVENT_SNACKBAR' :
      return Object.assign({}, state, { eventSnackbar: true });
    case 'DEACTIVATE_EVENT_SNACKBAR' :
      return Object.assign({}, state, { eventSnackbar: false });
    case 'ACTIVATE_FOOD_SNACKBAR' :
      return Object.assign({}, state, { foodSnackbar: true });
    case 'DEACTIVATE_FOOD_SNACKBAR' :
      return Object.assign({}, state, { foodSnackbar: false });
    case 'ACTIVATE_DELETE_SNACKBAR' :
      return Object.assign({}, state, { deleteSnackbar: true });
    case 'DEACTIVATE_DELETE_SNACKBAR' :
      return Object.assign({}, state, { deleteSnackbar: false });
    case 'ACTIVATE_PUBLIC_SNACKBAR' :
      return Object.assign({}, state, { publicSnackbar: true });
    case 'DEACTIVATE_PUBLIC_SNACKBAR' :
      return Object.assign({}, state, { publicSnackbar: false });
    case 'ACTIVATE_PRIVATE_SNACKBAR' :
      return Object.assign({}, state, { privateSnackbar: true });
    case 'DEACTIVATE_PRIVATE_SNACKBAR' :
      return Object.assign({}, state, { privateSnackbar: false });
    case 'UPDATE_EVENT_RESULTS' :
      return Object.assign({}, state, {eventResults: action.payload})
    case 'UPDATE_FOOD_RESULTS':
      return Object.assign({}, state, {foodResults: action.payload})
    case 'UPDATE_USERNAME':
      return Object.assign({}, state, { username: action.payload })
    case 'UPDATE_PASSWORD':
      return Object.assign({}, state, { password: action.payload })
    case 'AUTHEN':
      return Object.assign({}, state, { authenticated: true, password: '' });
    case 'LOGOUT':
      return initialState;
    case 'UPDATE_EVENT_QUERY':
      return Object.assign({}, state, {eventQuery: action.payload});
    case 'UPDATE_FOOD_QUERY':
      return Object.assign({}, state, {foodQuery: action.payload});
    case 'UPDATE_CITY' :
      return Object.assign({}, state, { city: action.payload });
    case 'UPDATE_TO_PUBLIC' :
      return Object.assign({}, state, { isPublic: true });
    case 'UPDATE_TO_PRIVATE' :
      return Object.assign({}, state, { isPublic: false });
    case 'UPDATE_TRIP_FROM_DATE':
      return Object.assign({}, state, { tripFromDate: action.payload })
    case 'UPDATE_TRIP_TO_DATE':
      return Object.assign({}, state, { tripToDate: action.payload })
    case 'SET_MIN_TO_DATE':
      return Object.assign({}, state, { minToDate: action.payload })
    case 'UPDATE_CITY':
      return Object.assign({}, state, { city: action.payload })
    case 'ACTIVATE' :
      return Object.assign({}, state, { activeTrip: { status: true, index: action.payload }})
    case 'DEACTIVATE' :
      return Object.assign({}, state, { activeTrip: { status: false, index: 0 } })
    case 'ERROR' :
      return Object.assign({}, state, { currentError: action.payload })
    case 'UPDATE_EVENT_QUERY':
      return Object.assign({}, state, { eventQuery: action.payload });
    case 'UPDATE_EVENT_RESULTS':
      return Object.assign({}, state, { eventResults: action.payload })
    case 'REFRESH_TRIP_EVENTS':
      const oldTrip = state.userTrips[state.activeTrip.index];
      const newTrip = Object.assign({}, oldTrip, { events: action.payload });
      const newTrips = state.userTrips.slice();
      newTrips.splice(state.activeTrip.index, 1, newTrip);
      return Object.assign({}, state, { userTrips: newTrips });
    case 'UPDATE_FOOD_QUERY':
      return Object.assign({}, state, { foodQuery: action.payload });
    case 'UPDATE_FOOD_RESULTS':
      return Object.assign({}, state, { foodResults: action.payload })
    case 'REFRESH_TRIP_EATIN':
      const oldTripEatin = state.userTrips[state.activeTrip.index];
      const newTripEatin = Object.assign({}, oldTripEatin, { eatin: action.payload });
      const newTripsEatin = state.userTrips.slice();
      newTripsEatin.splice(state.activeTrip.index, 1, newTripEatin);
      return Object.assign({}, state, { userTrips: newTripsEatin });
    case 'ACTIVATE_EVENT_SNACKBAR':
      return Object.assign({}, state, { eventSnackbar: true });
    case 'DEACTIVATE_EVENT_SNACKBAR':
      return Object.assign({}, state, { eventSnackbar: false });
    case 'ACTIVATE_FOOD_SNACKBAR':
      return Object.assign({}, state, { foodSnackbar: true });
    case 'DEACTIVATE_FOOD_SNACKBAR':
      return Object.assign({}, state, { foodSnackbar: false });
    case 'ACTIVATE_DELETE_SNACKBAR':
      return Object.assign({}, state, { deleteSnackbar: true });
    case 'DEACTIVATE_DELETE_SNACKBAR':
      return Object.assign({}, state, { deleteSnackbar: false });
    case 'ACTIVATE_PUBLIC_SNACKBAR':
      return Object.assign({}, state, { publicSnackbar: true });
    case 'DEACTIVATE_PUBLIC_SNACKBAR':
      return Object.assign({}, state, { publicSnackbar: false });
    default:
      return state;  //if unrecognized action type nothing happens
  }
}

export default reducer;<file_sep>/react-client/src/components/signup.jsx
import React from 'react';
import TextField from 'material-ui/TextField';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';

import * as theme from './homePage.jsx';  // * does all named exports from that file
import * as loginStyle from './login.jsx';
import * as searchStyle from './searchPage.jsx';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { cyan50, cyan100, cyan200, cyan300, cyan400, cyan500, cyan600, cyan700, cyan800, cyan900 } from 'material-ui/styles/colors';

class Signup extends React.Component {
	constructor (props) {
		super(props);

		this.state = {
			open: false
		}
	}

	submit (event) {
		event.preventDefault();
		this.props.signup(this.props.username, this.props.password);
		this.setState({ open: false });
		this.props.forward();
	}

	changeUsername (event) {
		this.props.updateUsername(event.target.value);
	}

	changePassword (event) {
		this.props.updatePassword(event.target.value);
	}

	handleOpen () {
		this.setState({ open: true });
	};

	handleClose () {
		this.setState({ open: false });
	};
	
	render () {
		const actions = [
			<FlatButton
				label="Join"
				primary={true}
				keyboardFocused={true}
				onClick={this.submit.bind(this)}
				style={loginStyle.styles.actionButtons}
			/>,
			<FlatButton
				label="Cancel"
				onClick={this.handleClose.bind(this)}
				style={loginStyle.styles.actionButtons}
			/>
		];

		return (
			<MuiThemeProvider muiTheme={theme.muiTheme}>
				<div>
					<RaisedButton
						label="Join"
						onClick={this.handleOpen.bind(this)}
						style={searchStyle.styles.navButtons}
					/>
					<Dialog 
						title="Sign Up"
						actions={actions}
						modal={false}
						open={this.state.open}
						onRequestClose={this.handleClose.bind(this)}>
						<label>Username:</label>
						<TextField id = "SUUsername" onChange = {this.changeUsername.bind(this)} />
						<br/>
						<label>Password:</label>
						<TextField id = 'SUPassword' type="password" onChange = {this.changePassword.bind(this)} />
					</Dialog>
				</div>
			</MuiThemeProvider>
		)
	}
}

export default Signup;<file_sep>/_README.md
# TripTeaze

> Pithy project description

## Usage

> Some usage instructions

## Requirements

- Node 0.10.x

## Development

npm run react-dev
npm run server-dev

### Installing Dependencies

From within the root directory:

npm install

you'll need a gitignored config.js file in the root directory for API keys: 'token' for eventbrite, 'zomatoKey' for zomato and 'mongo' a url to an mlab mongodb.  Get your own keys or ask us for ours.  

The heroku deployment uses process.env variables for these EVENTBRITE, ZOMATO and MONGODB_URI respectively, set those up on heroku

### Roadmap

-new found bug, when you delete events/activities on the userpage it deletes it from the wrong trip
-when you try and delete your last trip it does delete but it doesn't disappear right away
-more search options
-implement archiving of your own trips
-sorting of your trips
-comment/review system
-friending
-instagram api 4 more pics
-getting session stuff to work?
-passport
-anon user creating trips
-once trip dates have changed, have any events outside of the new date range removed
-Use archived parameter of trips somehow
-Make page transitions better


## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
<file_sep>/react-client/src/actions/index.js
import axios from 'axios';

////////////////////////////////HOME PAGE STUFF\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
const loading = () => ({type: 'TOGGLE_LOADING'});

const setUserTrips = (trips) => ({ type: 'SHOW_USER_TRIPS', payload: trips});

const setPublicTrips = (trips) => ({ type: 'SHOW_PUBLIC_TRIPS', payload: trips});

export const updateUsername = (username) => ({ type: 'UPDATE_USERNAME', payload: username });

export const updatePassword = (password) => ({ type: 'UPDATE_PASSWORD', payload: password });

export const login = (username, password) => {
  return (dispatch) => {
    return axios({
      method: 'get',
      url: '/login',
      params: {
        username: username,
        password: <PASSWORD>
      }
    }).then(
      results => {
        if (results.data.error) {
          alert(results.data.message);
        } else {
          dispatch(authenticate())
          dispatch(fetchTrips(username));
        }
      },
      error => { console.log('error', error); dispatch(badStuff(error)) }
    );
  }
};

export const signup = (username, password) => {
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/signup',
      data: {
        username: username,
        password: <PASSWORD>
      }
    }).then(
      results => {
        if (results.data.error) {
          alert(results.data.message);
        } else {
          dispatch(authenticate());
        }
      },
      error => dispatch(badStuff(error))
    );
  };
}

export const authenticate = () => ({ type: 'AUTHEN' });

export const logOut = () => {
  return (dispatch) => {
    return axios({
      method: 'get',
      url: 'logout',
    }).then(
      results => {
        dispatch(deauthenticate());
      }
    )
  }
}

export const deauthenticate = () => ({ type: 'LOGOUT' });

export const badStuff = (error) => ({type: 'ERROR', payload: error});

/////////////////////////////SEARCH PAGE STUFF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

/*****************************MAKE A TRIP *************************************/
export const fetchTrips = (param) => {
  return (dispatch) => {
    dispatch(loading());
    return axios({
      method: 'get',
      url: '/trips',
      params: {
        search: param
      }
    }).then(
      results => {
        if (param === 'public') {
          dispatch(setPublicTrips(results.data.trips));
          dispatch(loading());
        } else {
          dispatch(setUserTrips(results.data.trips));
          dispatch(loading());
        }
      },
      error => dispatch(badStuff(error))
    );
  }
};

export const updateTripDates = (user, city, fromDate, toDate) => {
  return dispatch => {
    return axios({
      method: 'patch',
      url: '/plan',
      data: {
        user: user,
        tripCity: city,
        tripFromDate: fromDate,
        tripToDate: toDate
      }
    }).then(
      results => {
        dispatch(updateFromDate(''));
        dispatch(updateToDate(''));
        dispatch(setMinToDate({}));
      },
      error => dispatch(badStuff(error))
    )
  }
}

export const updateFromDate = (date) => ({ type: 'UPDATE_TRIP_FROM_DATE', payload: date });

export const updateToDate = (date) => ({ type: 'UPDATE_TRIP_TO_DATE', payload: date });

export const setMinToDate = (date) => ({ type: 'SET_MIN_TO_DATE', payload: date });

export const updateCity = (city) => ({ type: 'UPDATE_CITY', payload: city });

export const makeNewTrip = (username, city, index, fromDate, toDate) => {
  return (dispatch) => {

    return axios({
      method: 'post',
      url: '/trips',
      data: {
        tripUser: username,
        tripCity: city,
        tripFromDate: fromDate,
        tripToDate: toDate
      }
    }).then(
      results => {
        dispatch(activateTrip(index));
        dispatch(fetchTrips(username));
        dispatch(makePrivate());
        dispatch(updateCity(''));
        dispatch(updateToDate(''));
        dispatch(setMinToDate({}));
        dispatch(updateFromDate(''));
      },
        error => dispatch(badStuff(error))
    );
  };
}


export const activateTrip = (tripIndex) => {
  //return { type: 'ACTIVATE', payload: tripIndex }
  return (dispatch) => {
    dispatch(updateFoodResults([]));
    dispatch(updateEventResults([]));
    dispatch(actuallyActivate(tripIndex));
  }
};

const actuallyActivate = (tripIndex) => ({ type: 'ACTIVATE', payload: tripIndex });

export const deactivate = () => {
  return (dispatch) => {
    dispatch(updateFoodResults([]));
    dispatch(updateEventResults([]));
    dispatch(actuallyDeactivate());
  }
};

const actuallyDeactivate = () => ({ type: 'DEACTIVATE' });

/***************************** EVENTS *************************************/

export const updateEventQuery = (query) => ({ type: 'UPDATE_EVENT_QUERY', payload: query });

export const searchEvents = (city, query, fromDate, toDate) => {
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/events',
      data: {
        tripCity: city,
        eventQuery: query,
        tripFromDate: fromDate,
        tripToDate: toDate
      }
    }).then(
      results => (dispatch(updateEventResults(results.data))),
      error => dispatch(badStuff(error))
    );
  };
}

const updateEventResults = (searchResults) => ({ type: 'UPDATE_EVENT_RESULTS', payload: searchResults });

export const addEventToTrip = (event, username, city) => {
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/events/add',
      data: {
        tripEvent: event,
        tripUser: username,
        tripCity: city
      }
    }).then(
      results => { 
        dispatch(fetchEventsFromTrip(username, city));
        dispatch(activateEventSnackbar());
      },
      error => dispatch(badStuff(error))
    );
  };
}

export const fetchEventsFromTrip = (username, city) => {
  //dispatch({ type: 'LOADING' });	
  return (dispatch) => {
    return axios({	
      method: 'get',
        url: '/events',
        params: {	
          tripUser: username,
          tripCity: city	
        }
      }).then(
        results => {dispatch(setTripEvents(results.data.events))},
        error => {dispatch(badStuff(err))}
      )
  }
}

const setTripEvents = (events) => ({ type: 'REFRESH_TRIP_EVENTS', payload: events });

export const activateEventSnackbar = () => ({type: 'ACTIVATE_EVENT_SNACKBAR'});

export const deactivateEventSnackbar = () => ({type: 'DEACTIVATE_EVENT_SNACKBAR'});

/***************************** FOOD *************************************/

export const updateFoodQuery = (query) => ({ type: 'UPDATE_FOOD_QUERY', payload: query });

export const searchForFood = (city, query) => {
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/foods',
      data: {
        tripCity: city,
        foodQuery: query
      }
    }).then(
      results => {
        dispatch(updateFoodResults(results.data.foods))},
      error => dispatch(badStuff(error))
    )
  }
}
  
const updateFoodResults = (searchResults) => ({ type: 'UPDATE_FOOD_RESULTS', payload: searchResults})

export const addFoodToTrip = (food, username, city) => {
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/foods/add',
      data: {
        tripFood: food,
        tripUser: username,
        tripCity: city
      }
    }).then(
      results => { 
        dispatch(fetchFoodFromTrip(username, city));
        dispatch(activateFoodSnackbar());
      },
      error => dispatch(badStuff(error))
    );
  };
}

export const fetchFoodFromTrip = (username, city) => {
  //dispatch({ type: 'LOADING' });	
  return (dispatch) => {
    return axios({
      method: 'get',
      url: '/foods',
      params: {
        tripUser: username,
        tripCity: city
      }
    }).then(
      results => { 
        dispatch(setTripEatin(results.data.foods)) },
      error => { dispatch(badStuff(err)) }
    )
  }
}

const setTripEatin = (foods) => ({ type: 'REFRESH_TRIP_EATIN', payload: foods });

export const activateFoodSnackbar = () => ({type: 'ACTIVATE_FOOD_SNACKBAR'});

export const deactivateFoodSnackbar = () => ({type: 'DEACTIVATE_FOOD_SNACKBAR'});

//////////////////////////////USER PAGE STUFF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

export const deleteTrip = (user, trip) => {
  return (dispatch) => {
    return axios ({
      method: 'patch',
      url: '/trips',
      data: {
        username: user,
        tripID: trip.id
      }
    }).then (
      results => {
        dispatch(fetchTrips(user));
        dispatch(activateDeleteSnackbar());
      },
      error => dispatch(badStuff(error))
    )
  }
}

export const deleteEvent = (event, username, city) => {
  console.log('delete!')
  return (dispatch) => {
    return axios ({
      method: 'post',
      url: '/events/remove',
      data: {
        eventID: event.id
      }
    }).then (
      results => { dispatch(fetchEventsFromTrip(username, city)) },
      error => {dispatch(badStuff(error))}
    );
  };
}

export const deleteFood = (food, username, city) => {
  console.log('delete!')
  return (dispatch) => {
    return axios({
      method: 'post',
      url: '/foods/remove',
      data: {
        foodID: food.id
      }
    }).then(
      results => { dispatch(fetchFoodFromTrip(username, city)) },
      error => { dispatch(badStuff(error)) }
    );
  };
}

export const toggleTripStatus = (user, trip) => {
  return dispatch => {
    return axios ({
      method: 'patch',
      url: '/trips',
      data: {
        user: user,
        tripCity: trip.city,
        public : !trip.isPublic
      }
    }).then (
      results => {
        dispatch(fetchTrips(user));
        trip.isPublic === false ? dispatch(activatePublicSnackbar()) : dispatch(activatePrivateSnackbar());
      },
      error => dispatch(badStuff(error))
    )
  }
}

export const makePublic = () => ({type: 'UPDATE_TO_PUBLIC'});

export const makePrivate = () => ({type: 'UPDATE_TO_PRIVATE'});


export const activateDeleteSnackbar = () => ({type: 'ACTIVATE_DELETE_SNACKBAR'});

export const deactivateDeleteSnackbar = () => ({type: 'DEACTIVATE_DELETE_SNACKBAR'});

export const activatePublicSnackbar = () => ({type: 'ACTIVATE_PUBLIC_SNACKBAR'});

export const deactivatePublicSnackbar = () => ({type: 'DEACTIVATE_PUBLIC_SNACKBAR'});

export const activatePrivateSnackbar = () => ({type: 'ACTIVATE_PRIVATE_SNACKBAR'});

export const deactivatePrivateSnackbar = () => ({type: 'DEACTIVATE_PRIVATE_SNACKBAR'});
  
//ACTION_NAME must correspond with reducer switch option

//  complex action example w/ async
// export const search = (searchTerm) => {
//   return (dispatch) => {
//     return makeAnAPIcall(searchTerm).then(
//       searchResults => dispatch(updateResults(searchResults)),
//       error => dispatch(badStuff(error))
//     );
//   };
// }

// const updateResults = (searchResults) => ({type: UPDATE_RESULTS, results: searchResults});
// const badStuff = (error) => ({type: ASYNC_ERROR, error: error});

// NOTES
// -dispatch is a Redux keyword for triggering actions
//   -it's actuallys being used under the hood but react-redux's mapDispatchToProps makes us
//     not have to say dispatch when we usually call action functions
// -actions are dispatched from the front-end (or here because of our middleware to handle asynchronicity)
//   the return statement is automatically passed onto the reducer under the parameter action
// -the capital names are action types, commonly reducers use this key in a switch to decide what to do
//   with the other parameters associated with the action (to modify the state)
<file_sep>/server/index.js
const express = require('express');
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const session = require('express-session');
const db = require('../database-mongo/index.js');
const eventbrite = require('../APIhelper/eventbrite.js');
const zomato = require('../APIhelper/zomatoHelper.js')
const path = require('path');
const moment = require('moment');

const app = express();
app.use(bodyParser.json());

app.use(session({
	secret: 'shhhhh af',
	resave: false,
	saveUnitialized: true
}));

app.use(express.static(__dirname + '/../react-client/dist'));

/***********************************************************************/
/*                        login                                        */

app.checkPassword = (userName, pw, checkPw) => {
  let match = false;
    let unhashedPw = bcrypt.compareSync(pw, checkPw);
    if (unhashedPw) {
      match = true;
    }
  return match;
}

app.get('/login', (req, res) =>{
  let userName = req.query.username
  let password = req.query.password

  db.retrieveUserPassword(userName, (err, userPw) => {
		if (err) {
			res.status(200).json({error: true, message: 'Sorry, we didn\'t recognize that username. Please try again!'});
		} else if (app.checkPassword(userName, password, userPw)) {
			req.session.loggedIn = true;
			res.status(200).end()
		} else {
			console.log('Unmatching username and password')
      res.status(200).json({error: true, message: 'Sorry, username and password do not match! Please try again!'});
		}
	})
})

app.get('/logout', (req, res) => {
   req.session.destroy((err) => {
     if (err) {
			 throw err;
			 res.status(500).send(err)
     }
   })
   res.end();
});

app.get('/plan', (req, res) => {
	res.sendFile(path.join(__dirname, '/../react-client/dist', 'index.html')); 
});

/*************************** SIGN UP STUFF ***************************/

// Sign up
app.post('/signup', (req, res) => {
	let username = req.body.username;
	let password = req.body.password;

	// Checks if the username already exists in the db
	db.userExists(username, (existingUser) => {
		// If the username already exists
		if (existingUser.length > 0) {
			console.log('Username already exists!');
			// Redirect to the signup page
			res.status(200).json({error: true, message: 'Sorry! username already in use! Please pick a different one!'})
		// Else if new user
		} else {
			// Hash the password
			bcrypt.hash(password, 10, (err, hash) => {
				if (err) {
					console.error('Error in hash password: ', err);
					res.status(200).json({error: true, message: 'Sorry! unknown error on our end! Please try again'});
				} else {
					// Store the new user/hash in the db
					db.addNewUser(username, hash);
					console.log(`User '${username}' added to database`);
					res.status(200).end()
				}
			});
		}
	});
});

// Creates new session after new user is added to the database
const createSession = (req, res, newUser) => {
	return req.session.regenerate(() => {
		req.session.user = newUser;
		// Redirects to home page
		res.redirect('/');
	});
}
/*************************** TRIP STUFF ***************************/
app.get('/trips', (req, res) => {
	const type = req.query.search; // right now tailored for public trips but can be adapted for user trips as well
	if (type === 'public') {
		db.showAllPublicTrips(function(err, data) {
			if (err  || !data) {
				res.status(500).send(err);
			} else {
				getTripsEvents(data, function (err, tripsEvents) {
					if (err) {
						res.status(500).send(err);
					} else {
						res.status(200).json({ trips: tripsEvents });
					}
				});
			}
		});
	} else if (!type) { 
		res.sendFile(path.join(__dirname, '/../react-client/dist', 'index.html'));
	} else {
		db.showUserTrips(type, function(err, data) {
			if (err || !data) {
				res.status(500).send(err);
			} else {
				getTripsEvents(data, function (err, fullTrips) {
					if (err) {
						res.status(500).send(err);
					} else {
						res.status(200).json({ trips: fullTrips });
					}
				});
			}
		});
	}
});

getTripsEvents = (trips, callback) => {
	let fullTrips = [];
	numFinished = 0;
	for (let i = 0; i < trips.length; i++) {
		fullTrips.push(Object.assign({}, {
			id : trips[i].id,
			city: trips[i].city,
			isArchived: trips[i].isArchived,
			isPublic: trips[i].isPublic,
			fromDate: trips[i].tripFromDate,
			toDate: trips[i].tripToDate
		}));

		const tripID = trips[i].id
		db.getTripEvents(tripID, function (err, events) {
			fullTrips[i].events = events;
			db.getTripRestaurants(tripID, function (err, food) {
				fullTrips[i].eatin = food;
				numFinished++;
				if (numFinished === trips.length) {
					callback(null, fullTrips);
				}
			});
		});
	}
}

app.post('/trips', (req, res) => {
	const user = (req.body.tripUser);
	const city = (req.body.tripCity);
	const fromDate = (req.body.tripFromDate);
	const toDate = (req.body.tripToDate);

	db.addNewTrip(user, city, fromDate, toDate, function(err, data) {
		if (err) {
			console.log(err);
			res.status(500).send(err);
		} else {
			res.status(200).json({ city: data.city });
		}
	});
});

app.patch('/trips', (req, res) => {
	if (req.body.public !== undefined) {

		db.modifyTripDetails(req.body.public, null, req.body.user, null, null, req.body.tripCity, function(err, data) {
			if (err) {
				res.status(500).send(err);
			} else {
				res.sendStatus(204);
			}
		});
	} else {
		db.remove('trip', req.body.tripID, function(err, data) {
			if (err) {
				res.status(500).send(err);
			} else {
				res.status(204).end();
			}
		})
	}
});

app.patch('/plan', (req, res) => {
	console.log('-----> server', req.body);
	db.modifyTripDetails(null, null, req.body.user, req.body.tripFromDate, req.body.tripToDate, req.body.tripCity, function(err, data) {
		if (err) {
			res.status(500).send(err);
		} else {
			res.sendStatus(204);
		}
	});
});

/******************************** Search - Events *****************************/

app.post('/events', function (req, res) {
	const city = req.body.tripCity;
	const query = req.body.eventQuery;
	const toDate = req.body.tripToDate;
	const fromDate = req.body.tripFromDate;
	eventbrite.searchEvents(query, city, fromDate, toDate, (err, data) => {

		if(err) {
			res.status(500).send(err);
		} else {
			res.status(200);
			res.status(200).json(data);
		}
	});
});

app.post('/events/remove', function (req, res) {

	db.remove('event', req.body.eventID, function(err) {
		if(err) {
			res.status(500).send(err);
		} else {
			res.status(200).end();
		}
	});
});

app.post('/events/add', function (req,res) {
	const event = req.body.tripEvent;
	const user = req.body.tripUser;
	const city = req.body.tripCity;

	db.addEventToTrip(event, user, city, function(err) {
		if (err) {
			console.log(err);
			res.status(500).send(err);
		} else {
			res.status(201).end();
		}
	});
});

app.get('/events', (req, res) => {
	const user = req.query.tripUser;
	const city = req.query.tripCity;

	db.showTripEvents(user, city, function(err, data) {
		if (err) {
			res.status(500).end(err);
		} else {
			res.status(200).json({ events: data });
		}
	});
});

/********************************* Search - Foods ***************************/

app.post('/foods', (req, res) => {
	let city =  req.body.tripCity;
	let searchFood = req.body.foodQuery;
	
	zomato.searchForCityId( city, ( err, data ) => {
		if (err) {
			res.status(500).send(err);
		} else {
			let cityId = data
			zomato.searchForFoods( cityId, searchFood, (err, result) => {
				if (err) {
					res.status(500).send(err);
				} else {
					res.status(200).json({foods: result})
				}
			});
		}
	});
});

app.post('/foods/remove', function (req, res) {
	
	db.remove('restaurant', req.body.foodID, function (err) {
		if (err) {
			res.status(500).send(err);
		} else {
			res.status(200).end();
		}
	});
});

app.post('/foods/add', function (req, res) {
	const food = req.body.tripFood;
	const user = req.body.tripUser;
	const city = req.body.tripCity;
	db.addRestaurantToTrip(food, user, city, function (err) {
		if (err) {
			res.status(500).send(err);
		} else {
			res.status(201).end();
		}
	});
});

app.get('/foods', (req, res) => {
	const user = req.query.tripUser;
	const city = req.query.tripCity;
	db.showTripRestaurants(user, city, function (err, data) {
		if (err) {
			res.status(500).send(err);
		} else {
			res.status(200).json({ foods: data });
		}
	});
});

+app.get('/*', function (req, res) {
	res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

/****************************************************************************/
const port = process.env.PORT || 3000;

app.listen(port, function() {
  console.log('listening on port 3000!');
});
<file_sep>/react-client/src/components/searchPage.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import moment from 'moment';

import RaisedButton from 'material-ui/RaisedButton';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import FlatButton from 'material-ui/FlatButton';
import Drawer from 'material-ui/Drawer';
import Dialog from 'material-ui/Dialog';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import DatePicker from 'material-ui/DatePicker';
import Toggle from 'material-ui/Toggle';
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';

import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { cyan50, cyan100, cyan200, cyan300, cyan400, cyan500, cyan600, cyan700, cyan800, cyan900 } from 'material-ui/styles/colors';
import * as theme from './homePage.jsx';  // * does all named exports from that file
import * as tripStyle from './trip.jsx';

import * as actions from '../actions/index.js';
import Activity from './activity.jsx';
import UserPage from './userPage.jsx';
import Events from './events.jsx';
import Signup from './signup.jsx';
import Login from './login.jsx';
import Eatin from './restaurants.jsx';

export const styles = {
  activityContainer: {
    display: 'inline-block',
    marginBottom: '1%',
    marginLeft: '2%',
    marginTop: '1%',
    verticalAlign: 'top',
    width: '47%'
  },
  activityTitle: {
    backgroundColor: '#f9f9f9',
    color: cyan800,
    fontSize: 20,
    fontWeight: 'bold',
    padding: '1%',
    margin: '2%',
    textAlign: 'left'
  },
  cardTitle: {
    fontSize: 15,
    fontWeight: 'bold',
    lineHeight: '0 !important'
  },
  createTripCard: {
    display: 'inline-block',
    marginLeft: '19%',
    marginTop: '1%',
    width: '30%',
  },
  existingTripsCard: {
    display: 'inline-block',
    marginTop: '1%',
    marginLeft: '2%',
    verticalAlign: 'top',
    width: '30%',
  },
  navButtons: {
    marginRight: '1em',
    marginLeft: '1em'
  },
  searchBar: {
    paddingLeft: '5%',
    paddingRight: '5%'
  },
  searchInput: {
    width: '80%'
  },
  searchResults: {
    margin: '2%'
  },
  tripDatesCard: {
    display: 'flex',
    flexFlow: 'column wrap'
  },
  tripDatesHeaders: {
    backgroundColor: '#f9f9f9',
    color: cyan900,
    fontSize: 15,
    fontWeight: 'bold',
    padding: '1%'
  },
  welcomeUser: {
    marginTop: '1%'
  }
}

//this is a terrible nightmare component that should be overhauled
class SearchPage extends React.Component {
  constructor (props) {
    super(props);
    if (props.store.userTrips.length !== 0  && props.store.activeTrip.status) {
      this.state = {
        open: true,
        activeCity: props.store.userTrips[props.store.activeTrip.index].city,
        dropdown: props.store.userTrips[props.store.activeTrip.index].city,
        activeFromDate: props.store.userTrips[props.store.activeTrip.index].fromDate,
        activeToDate: props.store.userTrips[props.store.activeTrip.index].toDate,
        editDatesOpen: false,
        tempFromDate: props.store.userTrips[props.store.activeTrip.index].fromDate,
        tempToDate: props.store.userTrips[props.store.activeTrip.index].toDate
      }
    } else if (props.store.userTrips.length !== 0) {
      this.state = {
        open: false,
        activeCity: props.store.userTrips[props.store.activeTrip.index].city,
        dropdown: 0,
        activeFromDate: props.store.userTrips[props.store.activeTrip.index].fromDate,
        activeToDate: props.store.userTrips[props.store.activeTrip.index].toDate,
        editDatesOpen: false,
        tempFromDate: props.store.userTrips[props.store.activeTrip.index].fromDate,
        tempToDate: props.store.userTrips[props.store.activeTrip.index].toDate
      }
    } else {
      this.state = {
        open: false,
        dropdown: 0,
        editDatesOpen: false
      }
    }
  }

  //works for input box and dropdown menu
  updateCity (event, index, value) {
    let store = this.props.store;
    let actions = this.props.actions;
    if (value && index !== 0) { //is dropdown
      this.setState({dropdown: value, open: true});
      this.setState({activeCity: store.userTrips[index - 1].city});
      this.setState({activeFromDate: store.userTrips[index - 1].fromDate, activeToDate: store.userTrips[index - 1].toDate});
      actions.updateCity('');
      actions.activateTrip(index - 1);
    } else if (index === 0) { //top option of dropdown
      this.setState({ dropdown: value });
      actions.deactivate();
    } else { //is textbox
      actions.updateCity(this.formatCity(event.target.value));
    }
  }

  //makes cities always only have a capital letter as the first char of each word
  formatCity (city) {
    const words = city.split(' ');
    let newWords = [];
    for (let word of words) {
      word = word.slice(0,1).toUpperCase().concat(word.slice(1).toLowerCase());
      newWords.push(word);
    }
    return newWords.join(' ');
  } 

  //creates a new trip from the input box
  submit (event) { 
    let store = this.props.store;
    let actions = this.props.actions;
    event.preventDefault();
    if (store.authenticated) {
      if (store.city !== '' && store.tripFromDate !== '' && store.tripToDate !== '') { //checks for required entries
        actions.makeNewTrip(store.username, store.city, store.userTrips.length, store.tripFromDate, store.tripToDate);
        this.setState({ activeCity: store.city, open: true, activeFromDate: store.tripFromDate, activeToDate: store.tripToDate});
      } else {
        window.alert('Please make sure to fill in the dates and city name!');
      }
    }
  };

/***************************** Event - search **********************************/
  updateEventQuery(event) {
    this.props.actions.updateEventQuery(event.target.value)
  };

  submitEventQuery (event) {
    let store = this.props.store;
    let actions = this.props.actions;

    event.preventDefault();  //prevent refresh, might not need this anymore
    if ((store.activeTrip.status || store.city) && store.eventQuery) {
      let city = store.activeTrip.status ? this.state.activeCity : store.city; //lets you maybe search on a city without creating a trip
      actions.searchEvents(this.state.activeCity, store.eventQuery, this.state.activeFromDate, this.state.activeToDate);
    } else {
      window.alert('Please select a city and search terms first!');
    }
  };

/***************************** Food - search **********************************/
  updateFoodQuery (event) {
    this.props.actions.updateFoodQuery(event.target.value)
  };

  submitFoodQuery (event) {
    let store = this.props.store;
    event.preventDefault();

    if(store.activeTrip.status || store.city) {
      let city = store.activeTrip.status ? this.state.activeCity : store.city;
      this.props.actions.searchForFood(this.state.activeCity, store.foodQuery)
    } else {
      window.alert('Please select a city and search terms first!')
    }
  };

/***************************** MESSAGE *****************************/
  render () {
    let message =  '';
    let messageEvents = '';
    let messageFood = '';
    let activeCity = this.state.activeCity;
    let store = this.props.store; //the redux store
    let actions = this.props.actions;

    if (!store.activeTrip.status) {
      message = 'Pick a city for your trip!';
      messageEvents = 'First pick a city before searching events!';
      messageFood = '';
    } else {
      message = `You\'re going to ${activeCity}! \n Or plan a different trip: `;
      messageEvents = `Type a keyword to find events in ${activeCity}!`;
      messageFood= `Or search for food in ${activeCity}!`;
    }

  /*************************** DATE SELECTION STUFF ***************************/
  // all this probably shouldn't be in the render method but oh well
    let today = new Date();

    let formatDate = (date) => {
      // Dates need to be in YYYY-MM-DD format
      return moment(date).format('YYYY-MM-DDT00:00:00.000Z'); //<-- moment is cool library
    }

    let updateFromDate = (event, date) => { //pulls from material date pickker
      let fromDate;
      if (date !== '') {
        fromDate = formatDate(date);
      } else {
        fromDate = '';
      }

      actions.updateFromDate(fromDate); //update store

      // This sets minimum "To" date based on the current "From" date in the correct date format
      if (date !== '') {
        actions.setMinToDate(date);
      } else {
        actions.setMinToDate({});
      }

      this.setState({
        activeFromDate: fromDate
      });
    }

    const updateToDate = (event, date) => {
      let toDate;
      if (date !== '') {
        toDate = formatDate(date);
      } else {
        toDate = '';
      }
      actions.updateToDate(toDate);

      this.setState({
        activeToDate: toDate
      });
    };
    //some of this is just trying to keep this component's state and the redux store in sync which is 
    //kind of bad form for redux for this component to have so much stuff
    const submitEditDates = () => {
      let city = this.state.activeCity;
      let newFromDate = store.tripFromDate;
      let newToDate = store.tripToDate;

      // updates trip dates in the db
      actions.updateTripDates(store.username, city, newFromDate, newToDate);

      store.userTrips[store.activeTrip.index].fromDate = store.tripFromDate;
      store.userTrips[store.activeTrip.index].toDate = store.tripToDate;

      this.setState({
        activeFromDate: newFromDate,
        activeToDate: newToDate,
        tempFromDate: newFromDate,
        tempToDate: newToDate,
        editDatesOpen: false
      });
    };

    /*************************** EXISTING TRIPS DROPDOWN ***************************/
    const dropdown = () => {
      if (store.authenticated) {  //logged in
        return (
          <div>
            <SelectField 
              value={this.state.dropdown} 
              onChange = {this.updateCity.bind(this)}
            > 
              <MenuItem primaryText = 'Make a New Trip' />
                {store.userTrips.map((trip, index) => 
                  <MenuItem
                    key = {index}
                    value = {trip.city}
                    primaryText = {trip.city} 
                  />
                )}
            </SelectField>
            <br/>
            <RaisedButton
              onClick={() => (this.setState({ open: !this.state.open }))}
              label='Show Details'
              disabled={!store.activeTrip.status}
            />
          </div>
        )
      } else {
        return (
          <div>Please login to view your current trips!</div>
        )
      }
    }

    /*************************** TRIP DETAILS SIDEBAR ***************************/
    //edit a trip's dates modal buttons
    const editDateActions = [
      <FlatButton
        label="Submit"
        primary={true}
        keyboardFocused={true}
        onClick={submitEditDates}
      />,
      <FlatButton
        label="Cancel"
        primary={true}
        style={{marginLeft: '2%'}}
        onClick={() => {
          updateFromDate(null, '');
          updateToDate(null, '');
          this.setState({
            editDatesOpen: false,
          });
        }}
      />
    ];

    const drawer = () => {
      if (store.activeTrip.status) {
        let activeTrip = store.userTrips[store.activeTrip.index]; 
        if (activeTrip) {
          let fromDate = moment(activeTrip.fromDate).format('MM/DD/YY');
          let toDate = moment(activeTrip.toDate).format('MM/DD/YY');
          return (
            <Drawer
              width={400}
              openSecondary={true}
              open={this.state.open}
            >
              <AppBar
                title={this.state.activeCity}
                iconElementLeft={
                  <IconButton
                    onClick={() => (this.setState({ open: false }))}
                  >
                    <NavigationClose />
                  </IconButton>}
              />
              <div style={{
                margin: '2%',
                fontSize: 25,
                fontWeight: 'bold',
                textAlign: 'center'
              }}>
                {fromDate} - {toDate}
                <div style={{display: 'inline-block', marginLeft: '1%'}}>
                  <FlatButton
                    label="Edit"
                    onClick={() => this.setState({editDatesOpen: true})}
                    backgroundColor='transparent'
                    hoverColor='#f9f9f9'
                  />
                  <Dialog
                    title="Modify Trip Dates"
                    actions={editDateActions}
                    modal={false}
                    open={this.state.editDatesOpen}
                    onRequestClose={() => this.setState({editDatesOpen: false})}
                  >
                    <div
                      style={{color: cyan800}}
                    >Trip Dates for {this.state.activeCity}:
                    </div>

                    <div style={{
                      color: cyan900,
                      fontWeight: 'bold'
                    }}>{fromDate} - {toDate}</div>

                    <br/><br/>

                    Edit your trip dates below:
                    <DatePicker
                      floatingLabelText="From"
                      autoOk={true}
                      onChange={updateFromDate}
                      minDate={today}
                    />
                    <DatePicker
                      floatingLabelText="To"
                      autoOk={true}
                      onChange={updateToDate}
                      minDate={store.minToDate}
                    />
                  </Dialog>
                </div>
              </div>

              {showActivityDiv('event', activeTrip)}
              <div style={tripStyle.styles.tripDetails}>
                {activeTrip.events.map((event, index) => 
                  (<Activity
                    key={index}
                    sidebar = 'true'
                    type='event'
                    activity={event}
                    user={store.username}
                    city={this.state.activeCity}
                    deleteEvent={actions.deleteEvent}
                  />))}
              </div>
              
              {showActivityDiv('eatin', activeTrip)}
              <div style={tripStyle.styles.tripDetails}>
                {activeTrip.eatin.map((eatin, index) => 
                  (<Activity
                    key={index}
                    sidebar='true'
                    type='food'
                    user={store.username}
                    city={this.state.activeCity}
                    deleteFood={actions.deleteFood}
                    activity={eatin}
                  />))}
              </div>
            </Drawer>
          );
        }
      }
    }
    //differ for logged in and out users
    const navLinks = () => {
      if (store.authenticated) {
        return (
          <div style={theme.styles.navLinks}>
            <Link to='/'>
              <RaisedButton
                label="Home"
              />
            </Link>
            <Link to='trips'>
              <RaisedButton
                label="My Trips"
                disabled={!store.authenticated}
                style={styles.navButtons}
              />
            </Link>
            <Link to='/'>
              <RaisedButton
                disabled={!store.authenticated}
                onClick={actions.logOut}
                label='Log Out'
              />
            </Link>
          </div>
        );
      } else {
        return (
          <div style={theme.styles.navLinks}>
            <Link to='/'>
              <RaisedButton
                label="Home"
              />
            </Link>
            <Signup
              signup={actions.signup}
              username={store.username}
              password={<PASSWORD>}
              updateUsername={actions.updateUsername}
              updatePassword={actions.updatePassword}
            />
            <Login
              login={actions.login}
              username={store.username}
              password={<PASSWORD>}
              updateUsername={actions.updateUsername}
              updatePassword={actions.updatePassword}
            />
          </div>
        )
      }
    }

    /************************* ACTIVITY HEADER DIVS ******************************/
    const showActivityDiv = (activityType, trip) => {
      // If activity = event and there are events in the current trip
      if (activityType === 'event' && trip.events.length > 0) {
        return (
          <div style={tripStyle.styles.activityHeader}>Events:</div>
        )
      // If activity = eatin and there are restaurants in the current trip
      } else if (activityType === 'eatin' && trip.eatin.length > 0) {
        return (
          <div style={tripStyle.styles.activityHeader}>Food:</div>
        )
      }
    };

    /*************************** WELCOME USER TEXT ***************************/
    const welcomeUser = () => {
      if (store.authenticated) {
        return (
          <div style={theme.styles.discoverTrips}>Welcome back, {store.username}!</div>
        )
      } else {
        return (
          <div style={theme.styles.discoverTrips}>Welcome!</div>
        )
      }
    }

    /************************ CREATE TRIP SEARCH BUTTON ************************/
    const searchButton = () => {
      return (
        <RaisedButton
          onClick={this.submit.bind(this)}
          label='Create Trip'
          disabled={!store.authenticated}
        />
      )
    }

    /*************************** STUFF ON PAGE ***************************/
    return (
      <MuiThemeProvider muiTheme={theme.muiTheme}>
        <Paper>
          {/************************** NAVIGATION **************************/}
          {navLinks()}
          {/******************************* HEADER *******************************/}
          <div style={theme.styles.header}>
            <Link to="/" style={{textDecoration: 'none', color: cyan900}}>
              TripTeaze
            </Link>
          </div>

          <div style={styles.welcomeUser}>{welcomeUser()}</div>
          
          {/************************** CREATE TRIP CARD **************************/}
          <div style={styles.createTripCard}>
            {drawer()}
            <Card>
              <CardTitle
                title="Create New Trip"
                titleStyle={styles.cardTitle}
                actAsExpander={true}
                showExpandableButton={true}
              />
              <CardText
                expandable={true}
              >
                <div style={styles.tripDatesCard}>
                  <div style={styles.tripDatesHeaders}>Trip Dates:</div>
                  <div>
                    <DatePicker
                      floatingLabelText="From"
                      autoOk={true}
                      onChange={updateFromDate}
                      minDate={today}
                    />
                    <DatePicker
                      floatingLabelText="To"
                      autoOk={true}
                      onChange={updateToDate}
                      // defaultDate={} // set default "to" date as the "from" date?
                      minDate={store.minToDate}
                    />
                  </div>
                  <br/>
                  <div>
                    <div style={styles.tripDatesHeaders}> {message} </div>
                    <TextField
                      id='city'
                      value={store.city}
                      onChange={this.updateCity.bind(this)}
                    />
                    <br/>
                    {searchButton()}
                  </div>
                </div>
              </CardText>
            </Card>
          </div>

          {/************************** EXISTING TRIPS CARD **************************/}
          <div style={styles.existingTripsCard}>
            <Card
              initiallyExpanded={true}
            >
              <CardTitle
                title="Current Trips"
                titleStyle={styles.cardTitle}
                actAsExpander={true}
                showExpandableButton={true}
              />
              <CardText
                expandable={true}
              >
                {dropdown()}
              </CardText>
            </Card>
          </div>
          
          {/************************** EXPLORE SECTION **************************/}
          <div style={{marginTop: '3%'}}>
            <div style={theme.styles.discoverTrips}>Explore</div>
            {/************************** SEARCH EVENTS **************************/}
            <Paper style={styles.activityContainer}>
              <div style={styles.activityTitle}>Events</div>
              <div style={styles.searchBar}>
                <TextField
                  id = 'event'
                  onChange = {this.updateEventQuery.bind(this)}
                  inputStyle={{ width: '100%' }}
                  style={styles.searchInput}
                />
                <RaisedButton 
                  onClick={this.submitEventQuery.bind(this)} 
                  label='Search' 
                />
              </div>

              {/************************** EVENT RESULTS **************************/}
              <div style={styles.searchResults}>
                <Events
                  events={store.eventResults}
                  addEventToTrip={actions.addEventToTrip}
                  user={store.username}
                  city={this.state.activeCity}
                  eventSnackbar={store.eventSnackbar}
                  onRequestClose={actions.deactivateEventSnackbar}
                />
              </div>
            </Paper>

            {/************************** SEARCH EATIN **************************/}
            <Paper style={styles.activityContainer}>
            <div style={styles.activityTitle}>Restaurants</div>
              <div style={styles.searchBar}>
                <TextField
                  id='food'
                  onChange={this.updateFoodQuery.bind(this)}
                  inputStyle={{ width: '100%' }}
                  style={styles.searchInput}
                />
                <RaisedButton 
                  onClick={this.submitFoodQuery.bind(this)} 
                  label='Search'              
                />
              </div>

              {/************************** EATIN RESULTS **************************/}
              <div style={styles.searchResults}>
                <Eatin
                  restaurants={store.foodResults}
                  addFoodToTrip={actions.addFoodToTrip}
                  user={store.username}
                  city={this.state.activeCity}
                  foodSnackbar={store.foodSnackbar}
                  onRequestClose={actions.deactivateFoodSnackbar}
                />
              </div>
            </Paper>
          </div>
        </Paper>
      </MuiThemeProvider>
    );
  }  
}
//react-redux stuff

//state is the redux store
const mapStateToProps = state => (
  { store: state }
);

//dispatch is the movement of actions to the reducer
const mapDispatchToProps = dispatch => (
  { actions: bindActionCreators(actions, dispatch) }
);

export default connect(mapStateToProps, mapDispatchToProps)(SearchPage);