Home

Awesome

Eventlistenr

tested with rspec

Eventlistenr is a single-page web application inspired by Eventbrite. This event technology platform enables users to securely login/signup to accounts, host, bookmark, and register to events. Check it out! Eventlistenr Live

Eventlistenr Live 2

Technologies

Features and Implementation

User Login/Signup format

Session Form

const App = () => (
  <div>
    <Switch>
      <ProtectedRoute exact path="/events/new" component={ EventFormContainer } />
      <Route path="/events/:eventId" component={ EventShowContainer } />
      <ProtectedRoute path="/profile" component={ UserProfileContainer } />
      <AuthRoute path="/login" component={ SessionFormContainer } />
      <AuthRoute path="/signup" component={ SessionFormContainer } />
      <Route path="/" component={ HomePage } />
    </Switch>
    <Footer />
  </div>
);

Hm, what is ProtectedRoute and AuthRoute???

Well, the following are custom routes created to protect frontend routes. Specifically, this prevents logged in users from accessing the /login or /signup page.

With AuthRoute, if user is loggedIn? then render respective component specified in AuthRoute, else, redirect user to homepage.

Similarly, with ProtectedRoute, if user is loggedIn? then render component with respective props; else, redirect user to login page.

These routes can then be used in the App component as shown in the above code snippet.

import React from 'react';
import { connect } from 'react-redux';
import { Route, Redirect, withRouter } from 'react-router-dom';

const Auth = ({ component: Component, path, loggedIn }) => (
  <Route path={path} render={(props) => (
    !loggedIn ? (
      <Component {...props} />
    ) : (
      <Redirect to="/" />
    )
  )} />
);

const Protected = ({ component: Component, path, loggedIn }) => (
  <Route path={path} render={(props) => (
     loggedIn ? (
      <Component {...props} />
    ) : (
      <Redirect to="/login" />
    )
  )} />
);

const mapStateToProps = state => (
  {loggedIn: Boolean(state.session.currentUser)}
);

export const AuthRoute = withRouter(connect(mapStateToProps, null)(Auth));
export const ProtectedRoute = withRouter(connect(mapStateToProps, null)(Protected));

Create Events

Event form

class DropForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      uploadedFileCloudinaryUrl: '',
    };
  }

  onImageDrop(files) {
    this.setState({
      uploadedFileCloudinaryUrl: files[0],
    });
    this.handleImageUpload(files[0]);
  }

  // for more info: https://css-tricks.com/image-upload-manipulation-react/
  handleImageUpload(file) {
    let upload = request.post(CLOUDINARY_UPLOAD_URL)
                        .field('upload_preset', CLOUDINARY_UPLOAD_PRESET)
                        .field('file', file);

    upload.end((err, response) => {
      if (err) {
        console.error(err);
      }
      if (response.body.secure_url !== '') {
        this.props.updateUrl(response.body.secure_url);
        this.setState({
          uploadedFileCloudinaryUrl: response.body.secure_url,
        });
      }
    });
  }
const nullEvent = Object.freeze({
  events: {},
  errors: []
});

let currentState;

const eventReducer = (state = nullEvent, action) => {
  Object.freeze(state);

  switch(action.type){
    case RECEIVE_ALL_EVENTS:
      const events = action.events;
      return merge({}, state, { events });
    case RECEIVE_EVENT:
      const event = action.event;
      return merge({}, state, { event });
    case REMOVE_EVENT:
    // first dup current state, then find event and delete item.
      currentState = merge({}, state);
      delete currentState[action.event.id];
      return currentState;
    case RECEIVE_EVENT_ERRORS:
      const errors = action.errors;
      return Object.assign({}, state, { errors });
    case CLEAR_EVENT_ERRORS:
      return Object.assign({}, state, { errors: [] });
    default:
      return state;
  }
};

Home Page

Home page

Event Show Page

Show page

import { connect } from 'react-redux';
import { requestEvent } from '../../actions/event_actions';
import EventShow from './event_show';

const mapStateToProps = ({ event, session }, ownProps) => ({
  event: event.events[ownProps.match.params.eventId] || {},
  currentUser: session.currentUser,
});

const mapDispatchToProps = dispatch => ({
  requestEvent: id => dispatch(requestEvent(id))
});

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

Bookmarking Events

bookmarkIcon() {
  const { currentUser } = this.props;

  if ( currentUser.bookmarks.includes(this.props.event.id)) {
    return (
      <i className="fa fa-heart selected" aria-hidden="true"></i>
    );
  } else {
    if (currentUser.id === undefined) {
      return (
        <Link to="/login">
          <i className="fa fa-heart-o unselected" aria-hidden="true"></i>
        </Link>
      );
    } else {
      return (
        <i className="fa fa-heart-o unselected" aria-hidden="true"></i>
      );
    }
  }
}

handleClick() {
  if (this.props.currentUser.id === undefined) {
    return
  } else if (this.props.currentUser.bookmarks.includes(this.props.event.id)) {
    this.props.deleteBookmark(this.props.event.id);
  } else {
    const event = { event_id: this.props.event.id };
    this.props.createBookmark(event);
  }
}

Registering to Events

Eventlistenr

handleChange(e) {
  e.preventDefault();
  this.setState({ quantity: parseInt(e.currentTarget.value) });
}

handleSubmit(e) {
  e.preventDefault();
  // console.log("ticket_form handleSubmit props", this.props);

  // create a ticket object before passing to createTicket()
  let ticket = Object.assign({}, this.state);
  ticket.event_id = this.props.event.id;
  this.props.createTicket(ticket)
  .then(() => {
    this.props.closeModal();
    this.props.history.push("/profile");
    })
}

User Dashboard

User Dashboard

<Route exact path={'/profile'}
  render={ () => <Redirect to={'/profile/ticketed'} /> }
/>
<Route path={'/profile/ticketed'}
  render={ () => <UpcomingEvents ticketedEvents={ticketedEvents} /> }
/>
<Route path={'/profile/bookmarked'}
  render={ () => <BookmarkedEvents bookmarkedEvents={bookmarkedEvents} /> }
/>
<Route path={'/profile/organized'}
  render={ () => <OrganizedEvents
  organizedEvents={organizedEvents} /> }
/>

Additional Resources

Future Direction

I plan to incorporate additional features to this application, such as the following: