Skip to content

MeetPup is an inspired clone of the site: Meet-UP. This project is an effort to display the work for the utilization of React, Redux, Express, Sequelize, PostgreSQL, and Render.

Notifications You must be signed in to change notification settings

BigDogEnergy/meetpup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MeetPup

MeetPup is an inspired clone of the site: Meet-UP. This project is an effort to display the work for the utilization of React, Redux, Express, Sequelize, PostgreSQL, and Render.

Live site: https://meetpup.onrender.com

Tech Stack

Database

Hosting

Getting Started

  1. Download and unzip
  2. Use the command npm start in both frontend and backend folders to run both servers
  3. Browser should automatically open at localhost:3000 to the Splash page.

Database Schema Design

Screenshot 2023-05-06 at 1 34 50 PM

API Documentation for BACKEND

USER AUTHENTICATION/AUTHORIZATION

All endpoints that require authentication

All endpoints that require a current user to be logged in.

  • Request: endpoints that require authentication
  • Error Response: Require authentication
    • Status Code: 401

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Authentication required"
      }

All endpoints that require proper authorization

All endpoints that require authentication and the current user does not have the correct role(s) or permission(s).

  • Request: endpoints that require proper authorization
  • Error Response: Require proper authorization
    • Status Code: 403

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Forbidden"
      }

Get the Current User

Returns the information about the current user that is logged in.

  • Require Authentication: true

  • Request

    • Method: GET
    • URL: /session
    • Body: none
  • Successful Response when there is a logged in user

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "firstName": "John",
          "lastName": "Smith",
          "email": "john.smith@gmail.com",
          "username": "JohnSmith"
        }
      }
  • Successful Response when there is no logged in user

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": null
      }

Log In a User

Logs in a current user with valid credentials and returns the current user's information.

  • Require Authentication: false

  • Request

    • Method: POST

    • URL: /session

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "credential": "john.smith@gmail.com",
        "password": "secret password"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "firstName": "John",
          "lastName": "Smith",
          "email": "john.smith@gmail.com",
          "username": "JohnSmith"
        }
      }
  • Error Response: Invalid credentials

    • Status Code: 401

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Invalid credentials"
      }
  • Error response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "email": "Email is required",
          "password": "Password is required"
        }
      }

Sign Up a User

Creates a new user, logs them in as the current user, and returns the current user's information.

  • Require Authentication: false

  • Request

    • Method: POST

    • URL: /users

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "firstName": "John",
        "lastName": "Smith",
        "email": "john.smith@gmail.com",
        "username": "JohnSmith",
        "password": "secret password"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "user": {
          "id": 1,
          "firstName": "John",
          "lastName": "Smith",
          "email": "john.smith@gmail.com",
          "username": "JohnSmith"
        }
      }
  • Error response: User already exists with the specified email

    • Status Code: 500

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "User already exists",
        "errors": {
          "email": "User with that email already exists"
        }
      }
  • Error response: User already exists with the specified username

    • Status Code: 500

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "User already exists",
        "errors": {
          "username": "User with that username already exists"
        }
      }
  • Error response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "email": "Invalid email",
          "firstName": "First Name is required",
          "lastName": "Last Name is required"
        }
      }

GROUPS

Get all Groups

Returns all the groups.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /groups
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Groups": [
          {
            "id": 1,
            "organizerId": 1,
            "name": "Evening Tennis on the Water",
            "about": "Enjoy rounds of tennis with a tight-nit group of people on the water facing the Brooklyn Bridge. Singles or doubles.",
            "type": "In person",
            "private": true,
            "city": "New York",
            "state": "NY",
            "createdAt": "2021-11-19 20:39:36",
            "updatedAt": "2021-11-19 20:39:36",
            "numMembers": 10,
            "previewImage": "image url"
          }
        ]
      }

Get all Groups joined or organized by the Current User

Returns all the groups.

  • Require Authentication: true

  • Request

    • Method: GET
    • URL: /groups/current
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Groups": [
          {
            "id": 1,
            "organizerId": 1,
            "name": "Evening Tennis on the Water",
            "about": "Enjoy rounds of tennis with a tight-nit group of people on the water facing the Brooklyn Bridge. Singles or doubles.",
            "type": "In person",
            "private": true,
            "city": "New York",
            "state": "NY",
            "createdAt": "2021-11-19 20:39:36",
            "updatedAt": "2021-11-19 20:39:36",
            "numMembers": 10,
            "previewImage": "image url"
          }
        ]
      }

Get details of a Group from an id

Returns the details of a group specified by its id.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /groups/:groupId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "organizerId": 1,
        "name": "Evening Tennis on the Water",
        "about": "Enjoy rounds of tennis with a tight-nit group of people on the water facing the Brooklyn Bridge. Singles or doubles.",
        "type": "In person",
        "private": true,
        "city": "New York",
        "state": "NY",
        "createdAt": "2021-11-19 20:39:36",
        "updatedAt": "2021-11-19 20:39:36",
        "numMembers": 10,
        "GroupImages": [
          {
            "id": 1,
            "url": "image url",
            "preview": true
          },
          {
            "id": 2,
            "url": "image url",
            "preview": false
          }
        ],
        "Organizer": {
          "id": 1,
          "firstName": "John",
          "lastName": "Smith"
        },
        "Venues": [
          {
            "id": 1,
            "groupId": 1,
            "address": "123 Disney Lane",
            "city": "New York",
            "state": "NY",
            "lat": 37.7645358,
            "lng": -122.4730327
          }
        ]
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Create a Group

Creates and returns a new group.

  • Require Authentication: true

  • Request

    • Method: POST

    • URL: /groups

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "name": "Beau's Secret Helpers",
        "about": "It takes a village to raise a wolf puppy. Especially one as spoiled as Beau.",
        "type": "In person",
        "private": false,
        "city": "San Diego",
        "state": "CA"
      }
  • Successful Response

    • Status Code: 201

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "organizerId": 1,
        "name": "Beau's Secret Helpers",
        "about": "It takes a village to raise a wolf puppy. Especially one as spoiled as Beau.",
        "type": "In person",
        "private": false,
        "city": "San Diego",
        "state": "CA",
        "createdAt": "2023-06-15T22:45:07.267Z",
        "updatedAt": "2023-06-15T22:45:07.267Z"
      }
  • Error Response: Body validation error

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "name": "Name must be 60 characters or less",
          "about": "About must be 50 characters or more",
          "type": "Type must be 'Online' or 'In person'",
          "private": "Private must be a boolean",
          "city": "City is required",
          "state": "State is required"
        }
      }

Add an Image to a Group based on the Group's id

Create and return a new image for a group specified by id.

  • Require Authentication: true

  • Require proper authorization: Current User must be the organizer for the group

  • Request

    • Method: POST

    • URL: /groups/:groupId/images

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "url": "a really cute dog image url",
        "preview": true
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "url": "a really cute dog image url",
        "preview": true
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Edit a Group

Updates and returns an existing group.

  • Require Authentication: true

  • Require proper authorization: Group must belong to the current user

  • Request

    • Method: PUT

    • URL: /groups/:groupId

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "name": "NEW DOG RELATED NAME IN ALL CAPS",
        "about": "Probably something related to running around Fiesta Island together, but we can settle for PB Dog Park",
        "type": "In person",
        "private": true,
        "city": "San Diego",
        "state": "CA"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
      "id": 8,
      "organizerId": 6,
      "name": "NEW DOG RELATED NAME IN ALL CAPS",
      "about": "Probably something related to running around Fiesta Island together, but we can settle for PB Dog Park",
      "type": "In person",
      "private": true,
      "city": "San Diego",
      "state": "CA",
      "createdAt": "2023-06-15T22:45:07.267Z",
      "updatedAt": "2023-06-15T22:49:44.038Z"
      }
  • Error Response: Body validation error

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "name": "Name must be 60 characters or less",
          "about": "About must be 50 characters or more",
          "type": "Type must be 'Online' or 'In person'",
          "private": "Private must be a boolean",
          "city": "City is required",
          "state": "State is required",
        }
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Delete a Group

Deletes an existing group.

  • Require Authentication: true

  • Require proper authorization: Group must belong to the current user

  • Request

    • Method: DELETE
    • URL: /groups/:groupId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted"
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

VENUES

Get All Venues for a Group specified by its id

Returns all venues for a group specified by its id

  • Require Authentication: true

  • Require Authentication: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: GET
    • URL: /groups/:groupId/venues
    • Headers:
      • Content-Type: application/json
    • Body: none
  • Successful Response

    • Status Code: 200
    • Headers:
      • Content-Type: application/json
    • Body:
    {
      "Venues": [
        {
          "id": 1,
          "groupId": 1,
          "address": "123 Disney Lane",
          "city": "New York",
          "state": "NY",
          "lat": 37.7645358,
          "lng": -122.4730327
        }
      ]
    }
    
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Create a new Venue for a Group specified by its id

Creates and returns a new venue for a group specified by its id

  • Require Authentication: true

  • Require Authentication: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: POST
    • URL: /groups/:groupId/venues
    • Headers:
      • Content-Type: application/json
    • Body:
    {
      "address": "1998 28th St",
      "city": "San Diego",
      "state": "CA",
      "lat": 32.72621,
      "lng": 32.72621
    } 
  • Successful Response

    • Status Code: 200
    • Headers:
      • Content-Type: application/json
    • Body:
    {
      "id": 8,
      "groupId": 6,
      "address": "1998 28th St",
      "city": "San Diego",
      "state": "CA",
      "lat": "32.7262100",
      "lng": "32.7262100"
    }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "address": "Street address is required",
          "city": "City is required",
          "state": "State is required",
          "lat": "Latitude is not valid",
          "lng": "Longitude is not valid"
        }
      }

Edit a Venue specified by its id

Edit a new venue specified by its id

  • Require Authentication: true

  • Require Authentication: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: /venues/:venueId
    • URL: PUT
    • Headers:
      • Content-Type: application/json
    • Body:
    {
      "address": "AWESOME NEW DOG FRIENDLY ADDRESS",
      "city": "Dog Island",
      "state": "CA",
      "lat": 37.7645358,
      "lng": -122.4730327
    }
    
  • Successful Response

    • Status Code: 200
    • Headers:
      • Content-Type: application/json
    • Body:
    {
      "id": "6",
      "groupId": 6,
      "address": "AWESOME NEW DOG FRIENDLY ADDRESS",
      "city": "Dog Island",
      "state": "CA",
      "lat": 37.7645358,
      "lng": -122.4730327
    }
  • Error response: Couldn't find a Venue with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Venue couldn't be found"
      }
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "address": "Street address is required",
          "city": "City is required",
          "state": "State is required",
          "lat": "Latitude is not valid",
          "lng": "Longitude is not valid"
        }
      }

EVENTS

Get all Events

Returns all the events.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /events
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Events": [
          {
            "id": 1,
            "groupId": 1,
            "venueId": null,
            "name": "Tennis Group First Meet and Greet",
            "type": "Online",
            "startDate": "2021-11-19 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 8,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": null,
          },
          {
            "id": 1,
            "groupId": 1,
            "venueId": 1,
            "name": "Tennis Singles",
            "type": "In Person",
            "startDate": "2021-11-20 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 4,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": {
              "id": 1,
              "city": "New York",
              "state": "NY"
            },
          },
        ]
      }

Get all Events of a Group specified by its id

Returns all the events of a group specified by its id

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /groups/:groupId/events
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Events": [
          {
            "id": 1,
            "groupId": 1,
            "venueId": null,
            "name": "Tennis Group First Meet and Greet",
            "type": "Online",
            "startDate": "2021-11-19 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 8,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": null,
          },
          {
            "id": 1,
            "groupId": 1,
            "venueId": 1,
            "name": "Tennis Singles",
            "type": "In Person",
            "startDate": "2021-11-20 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 4,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": {
              "id": 1,
              "city": "New York",
              "state": "NY"
            },
          },
        ]
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Get details of an Event specified by its id

Returns the details of an event specified by its id.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /events/:eventId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "groupId": 1,
        "venueId": 1,
        "name": "Tennis Group First Meet and Greet",
        "description": "First meet and greet event for the evening tennis on the water group! Join us online for happy times!",
        "type": "Online",
        "capacity": 10,
        "price": 18.50,
        "startDate": "2021-11-19 20:00:00",
        "endDate": "2021-11-19 22:00:00",
        "numAttending": 8,
        "Group": {
          "id": 1,
          "name": "Evening Tennis on the Water",
          "private": true,
          "city": "New York",
          "state": "NY"
        },
        "Venue": {
          "id": 1,
          "address": "123 Disney Lane",
          "city": "New York",
          "state": "NY",
          "lat": 37.7645358,
          "lng": -122.4730327,
        },
        "EventImages": [
          {
            "id": 1,
            "url": "image url",
            "preview": true
          },
          {
            "id": 2,
            "url": "image url",
            "preview": false
          }
        ],
      }
  • Error response: Couldn't find a Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }

Create an Event for a Group specified by its id

Creates and returns a new event for a group specified by its id

  • Require Authentication: true

  • Require Authorization: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: POST

    • URL: /groups/:groupId/events

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "venueId": 1,
        "name": "Scooby Doo's Marathon",
        "type": "Online",
        "capacity": 1000,
        "price": 69.95,
        "description": "Likely a Mario Kart Tournament",
        "startDate": "2024-11-19 20:00:00",
        "endDate": "2024-11-19 22:00:00"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 7,
        "groupId": 6,
        "venueId": 1,
        "name": "Scooby Doo's Marathon",
        "type": "Online",
        "capacity": 1000,
        "price": 69.95,
        "description": "Likely a Mario Kart Tournament",
        "startDate": "2024-11-19T20:00:00.000Z",
        "endDate": "2024-11-19T22:00:00.000Z"
      }
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "venueId": "Venue does not exist",
          "name": "Name must be at least 5 characters",
          "type": "Type must be Online or In person",
          "capacity": "Capacity must be an integer",
          "price": "Price is invalid",
          "description": "Description is required",
          "startDate": "Start date must be in the future",
          "endDate": "End date is less than start date"
        }
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Add an Image to a Event based on the Event's id

Create and return a new image for an event specified by id.

  • Require Authentication: true

  • Require proper authorization: Current User must be an attendee, host, or co-host of the event

  • Request

    • Method: POST

    • URL: /events/:eventId/images

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "url": "image url",
        "preview": false
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "url": "image url",
        "preview": false
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }

Edit an Event specified by its id

Edit and returns an event specified by its id

  • Require Authentication: true

  • Require Authorization: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: PUT

    • URL: /events/:eventId

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "venueId": 6,
        "name": "NEW BETTER NAME",
        "type": "Online",
        "capacity": 50,
        "price": 3.99,
        "description": "New and better name. We picked the best name, no one has better names than us.",
        "startDate": "2024-11-19 20:00:00",
        "endDate": "2024-11-19 21:00:00"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 7,
        "groupId": 6,
        "venueId": 6,
        "name": "NEW BETTER NAME",
        "capacity": 50,
        "price": 3.99,
        "description": "New and better name. We picked the best name, no one has better names than us.",
        "startDate": "2024-11-19T20:00:00.000Z",
        "endDate": "2024-11-19T21:00:00.000Z"
      }
  • Error Response: Body validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "venueId": "Venue does not exist",
          "name": "Name must be at least 5 characters",
          "type": "Type must be Online or In person",
          "capacity": "Capacity must be an integer",
          "price": "Price is invalid",
          "description": "Description is required",
          "startDate": "Start date must be in the future",
          "endDate": "End date is less than start date"
        }
      }
  • Error response: Couldn't find a Venue with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Venue couldn't be found"
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }

Delete an Event specified by its id

Delete an event specified by its id

  • Require Authentication: true

  • Require Authorization: Current User must be the organizer of the group or a member of the group with a status of "co-host"

  • Request

    • Method: DELETE
    • URL: /events/:eventId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted"
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }

MEMBERSHIPS

Get all Members of a Group specified by its id

Returns the members of a group specified by its id.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /groups/:groupId/members
    • Body: none
  • Successful Response: If you ARE the organizer or a co-host of the group. Shows all members and their statuses.

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Members": [
          {
            "id": 2,
            "firstName": "Clark",
            "lastName": "Adams",
            "Membership": {
              "status": "co-host"
            },
          },
          {
            "id": 3,
            "firstName": "John",
            "lastName": "Smith",
            "Membership": {
              "status": "member"
            },
          },
          {
            "id": 4,
            "firstName": "Jane",
            "lastName": "Doe",
            "Membership": {
              "status": "pending"
            },
          },
        ]
      }
  • Successful Response: If you ARE NOT the organizer of the group. Shows only members that don't have a status of "pending".

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Members": [
          {
            "id": 2,
            "firstName": "Clark",
            "lastName": "Adams",
            "Membership": {
              "status": "co-host"
            },
          },
          {
            "id": 3,
            "firstName": "John",
            "lastName": "Smith",
            "Membership": {
              "status": "member"
            },
          },
        ]
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }

Request a Membership for a Group based on the Group's id

Request a new membership for a group specified by id.

  • Require Authentication: true

  • Request

    • Method: POST
    • URL: /groups/:groupId/membership
    • Headers:
      • Content-Type: application/json
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "memberId": 2,
        "status": "pending"
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }
  • Error response: Current User already has a pending membership for the group

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Membership has already been requested"
      }
  • Error response: Current User is already an accepted member of the group

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "User is already a member of the group"
      }

Change the status of a membership for a group specified by id

Change the status of a membership for a group specified by id.

  • Require Authentication: true

  • Require proper authorization:

    • To change the status from "pending" to "member":
      • Current User must already be the organizer or have a membership to the group with the status of "co-host"
    • To change the status from "member" to "co-host":
      • Current User must already be the organizer
  • Request

    • Method: PUT

    • URL: /groups/:groupId/membership

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "memberId": 2,
        "status": "member"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "groupId": 1,
        "memberId": 2,
        "status": "member"
      }
  • Error response: If changing the membership status to "pending".

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validations Error",
        "errors": {
          "status" : "Cannot change a membership status to pending"
        }
      }
  • Error response: Couldn't find a User with the specified memberId

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation Error",
        "errors": {
          "memberId": "User couldn't be found"
        }
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }
  • Error response: If membership does not exist

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Membership between the user and the group does not exist"
      }

Delete membership to a group specified by id

Delete a membership to a group specified by id.

  • Require Authentication: true

  • Require proper authorization: Current User must be the host of the group, or the user whose membership is being deleted

  • Request

    • Method: DELETE

    • URL: /groups/:groupId/membership

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "memberId": 1
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted membership from group"
      }
  • Error response: Couldn't find a User with the specified memberId

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Validation Error",
        "errors": {
          "memberId": "User couldn't be found"
        }
      }
  • Error response: Couldn't find a Group with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group couldn't be found"
      }
  • Error response: Membership does not exist for this User

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Membership does not exist for this User"
      }

ATTENDEES

Get all Attendees of an Event specified by its id

Returns the attendees of an event specified by its id.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /events/:eventId/attendees
    • Body: none
  • Successful Response: If you ARE the organizer of the group or a member of the group with a status of "co-host". Shows all attendees including those with a status of "pending".

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Attendees": [
          {
            "id": 2,
            "firstName": "Clark",
            "lastName": "Adams",
            "Attendance": {
              "status": "attending"
            },
          },
          {
            "id": 3,
            "firstName": "John",
            "lastName": "Smith",
            "Attendance": {
              "status": "waitlist"
            },
          },
          {
            "id": 4,
            "firstName": "Jane",
            "lastName": "Doe",
            "Attendance": {
              "status": "pending"
            },
          },
        ]
      }
  • Successful Response: If you ARE NOT the organizer of the group or a member of the group with a status of "co-host". Shows all members that don't have a status of "pending".

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Attendees": [
          {
            "id": 2,
            "firstName": "Clark",
            "lastName": "Adams",
            "Attendance": {
              "status": "attending"
            },
          },
          {
            "id": 3,
            "firstName": "John",
            "lastName": "Smith",
            "Attendance": {
              "status": "waitlist"
            },
          },
        ]
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }

Request to Attend an Event based on the Event's id

Request attendance for an event specified by id.

  • Require Authentication: true

  • Require Authorization: Current User must be a member of the group

  • Request

    • Method: POST
    • URL: /events/:eventId/attendance
    • Headers:
      • Content-Type: application/json
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "userId": 2,
        "status": "pending"
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }
  • Error response: Current User already has a pending attendance for the event

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Attendance has already been requested"
      }
  • Error response: Current User is already an accepted attendee of the event

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "User is already an attendee of the event"
      }

Change the status of an attendance for an event specified by id

Change the status of an attendance for an event specified by id.

  • Require Authentication: true

  • Require proper authorization: Current User must already be the organizer or have a membership to the group with the status of "co-host"

  • Request

    • Method: PUT

    • URL: /events/:eventId/attendance

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "userId": 2,
        "status": "attending"
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "id": 1,
        "eventId": 1,
        "userId": 2,
        "status": "attending"
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }
  • Error response: If changing the attendance status to "pending".

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Cannot change an attendance status to pending"
      }
  • Error response: If attendance does not exist

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Attendance between the user and the event does not exist"
      }

Delete attendance to an event specified by id

Delete an attendance to an event specified by id.

  • Require Authentication: true

  • Require proper authorization: Current User must be the host of the group, or the user whose attendance is being deleted

  • Request

    • Method: DELETE

    • URL: /events/:eventId/attendance

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "userId": 1
      }
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted attendance from event"
      }
  • Error response: Couldn't find an Event with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event couldn't be found"
      }
  • Error response: Attendance does not exist for this User

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Attendance does not exist for this User"
      }
  • Error response: Only the User or organizer may delete an Attendance

    • Status Code: 403

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Only the User or organizer may delete an Attendance"
      }

IMAGES

Delete an Image for a Group

Delete an existing image for a Group.

  • Require Authentication: true

  • Require proper authorization: Current user must be the organizer or "co-host" of the Group

  • Request

    • Method: DELETE
    • URL: /images/group-images/:imageId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted"
      }
  • Error response: Couldn't find an Image with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Group Image couldn't be found"
      }

Delete an Image for an Event

Delete an existing image for an Event.

  • Require Authentication: true

  • Require proper authorization: Current user must be the organizer or "co-host" of the Group that the Event belongs to

  • Request

    • Method: DELETE
    • URL: /images/event-images/:imageId
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Successfully deleted"
      }
  • Error response: Couldn't find an Image with the specified id

    • Status Code: 404

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Event Image couldn't be found"
      }

Add Query Filters to Get All Events

Return events filtered by query parameters.

  • Require Authentication: false

  • Request

    • Method: GET
    • URL: /events
    • Query Parameters
      • page: integer, minimum: 1, maximum: 10, default: 1
      • size: integer, minimum: 1, maximum: 20, default: 20
      • name: string, optional
      • type: string, optional
      • startDate: string, optional
    • Body: none
  • Successful Response

    • Status Code: 200

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "Events": [
          {
            "id": 1,
            "groupId": 1,
            "venueId": null,
            "name": "Tennis Group First Meet and Greet",
            "type": "Online",
            "startDate": "2021-11-19 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 8,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": null,
          },
          {
            "id": 1,
            "groupId": 1,
            "venueId": 1,
            "name": "Tennis Singles",
            "type": "In Person",
            "startDate": "2021-11-20 20:00:00",
            "endDate": "2021-11-19 22:00:00",
            "numAttending": 4,
            "previewImage": "image url",
            "Group": {
              "id": 1,
              "name": "Evening Tennis on the Water",
              "city": "New York",
              "state": "NY"
            },
            "Venue": {
              "id": 1,
              "city": "New York",
              "state": "NY",
            },
          },
        ]
      }
  • Error Response: Query parameter validation errors

    • Status Code: 400

    • Headers:

      • Content-Type: application/json
    • Body:

      {
        "message": "Bad Request", // (or "Validation error" if generated by Sequelize),
        "errors": {
          "page": "Page must be greater than or equal to 1",
          "size": "Size must be greater than or equal to 1",
          "name": "Name must be a string",
          "type": "Type must be 'Online' or 'In Person'",
          "startDate": "Start date must be a valid datetime",
        }
      }

Functional Features in Frontend

Groups

Create a Group

20626049-9406-4EA8-A3EA-4916E2E60F55

Read all Groups

AC93CEBB-D9C7-4AFA-822F-29A062F91A4C

Update a Group

520D74C3-C28E-4B4B-B861-FC0CDC0ABEE0

Delete a Group

Events

Create an Event

7E8024DA-FD89-4440-997A-1FBBDBFC3D44

Read all Events

086BDF11-F954-4327-93E5-B14351DF1317

Delete an Event

C21EF62A-A809-4561-AD44-2DAFAC21F0A1

React Components List

Splash

Our landing Page DD734BE8-9C2C-4D9D-BFB5-8CE53AEC8A7D

##Navigation Our Navigation component is hosted at the top of ever page to allow for easy site maneuverability

SignUp form Modal

Upon attempting to Sign Up for MeetPup, the SignUp form Modal opens and displays our Sign Up Form Page

SignUp form Page

Creates a new User to navigate site.

Login form Modal

If a new user already has a log-in, they may browse their groups and events via the Login Form Modal

Login Form Page

the login modal hosts the Login Form Page for all users

Create Group Form

From our landing page, Users may create a group via this form component

Groups

Groups component allows us to view all Groups, which loads our Single Card component

Single Card

Creates an individual card for each group within our database

GroupCards

Upon clicking any card, user is redirected to our Group Cards component which hosts group details our group can be updated here via the Edit Group Form component and deleted.

Update Group Form

Confirmation Modal

upon attempt to delete a group, a modal component was created to verify desire to delete a group.

Single Event Card

Within our group details component, we host our Single Event Card which redirects to our Single Event Detail component for each of our upcoming and past events for each group

##Single Event Detail Hosts our detailed Event component which allows us to read single events and methods for deleting. Upon completion of the update Event component, it will also be available through Single Event Detail

Events

Events component allows us to view all Events, both upcoming at the top and past events at the bottom

FrontEnd Routes

Below is our Front End paths

<> {isLoaded && (

      <Route exact path='/groups/new'>
        <CreateGroupForm />
      </Route>

      <Route exact path='/groups/:groupId/events/new'>
        <CreateEventForm />
      </Route>

      <Route path="/login">
        <LoginFormPage />
      </Route>
      
      <Route path="/signup">
        <SignupFormPage />
      </Route>
      
      <Route exact path="/">
        <Splash />
      </Route>

      <Route path='/groups/:groupId/edit'>
        <EditGroupForm groups={groups}/>
      </Route>

      <Route exact path='/groups/:groupId'>
        <SingleCard />
      </Route>

      <Route path='/events/:eventId/edit'>
        <EditEventForm />
      </Route>

      <Route exact path='/events/:eventId'>
        <SingleEventDetail />
      </Route>

      <Route exact path='/groups'>
        <Groups />
      </Route>

      <Route path='/events'>
        <Events />
      </Route>
    
    </Switch>
  )}

Redux Store Tree

export const rootReducer = combineReducers({ session: sessionReducer, group: groupReducer, events: eventReducer });

Future Features

We will soon implement Memberships, Attendance, Venues full CRUDS and Update CRUD for our Events component

Technical Details

A few of the greatest challenges faced during this project composition includes conditional loading, proper utilization and construction of state, and breaking down each step within project construction. I would frequently find I had tangentially moved to another component or task while coding this project

Another challenge and accomplished feat involved solving how to best format our Date and Time information to meet the requirements within each component. These were the helper functions composed to complete this task:

 //date and time helper functions
    function splitDateTime(dateTimeString) {
        const [date, fullTime] = dateTimeString.split('T');
        const time = fullTime ? fullTime.split('.')[0] : '';
        return { date, time } ;
    }
    
    function splitEndTime(dateTimeString) {
        const [endDate, fullTime] = dateTimeString.split('T');
        const endTime = fullTime ? fullTime.split('.')[0] : '';
        return { endDate, endTime } ;
    }

    function convertToAMPM(timeString) {
        if (!timeString) return '';
        const [hour, minute] = timeString.split(':');
        let amOrPm = 'AM';
        let adjustedHour = parseInt(hour, 10);
    
        if (adjustedHour >= 12) {
            amOrPm = 'PM';
            if (adjustedHour > 12) {
                adjustedHour -= 12;
            }
        }
        
        return `${adjustedHour}:${minute} ${amOrPm}`;
    }

A section of code I wish to revise in the future for efficiency includes my Group Detail component which also hosts all upcoming events and past events. Rewriting my store to hold this information in state without having to make multiple calls to the backend would drastically increase the functioning speed of my application:


{showModal && (
            <Modal onClose={() => setShowModal(false)}>
                {modalContent === 'joinGroup' && (
                    <div className='feature-coming-soon'>
                        <div>Feature coming soon...</div>
                    </div>
                )}

                {modalContent === 'deleteConfirmation' && (
                    <div className="confirm-modal-content">
                        <div className="confirm-modal-title">
                            Confirm Delete
                        </div>
                        <div className="confirm-modal-text">
                            Are you sure you want to delete this group?
                        </div>
                        <div className="confirm-modal-option-1">
                            <button className='single-card-crud-event-red' onClick={handleConfirmDelete}>Confirm (Delete Group)</button>
                        </div>
                        <div className="confirm-modal-option-2">
                            <button className='single-card-crud-event' onClick={() => setShowModal(false)}>Cancel (Keep Group)</button>
                        </div>

                    </div>
                )}
            </Modal>
        )}

        {( loading || groupLoading) ? (
            <div className="loading-container">
                Loading...
            </div>
        ) : (
            <div className="single-eventDetail-container">
                <div className="event-header-container">
                    <div className='redirect-to-allCards'> 
                        <div className='single-link-arrow'>&lt;</div>
                        <Link className='single-event-link' to='/groups'>Groups </Link> 
                    </div>
                </div>
                <div className='single-card-container'>
                    <div className='single-card-top'>
                        <img className='single-card-image' src={preview} alt="No Image"/>
                        <div className='single-card-top-info'>
                            <div className='single-card-top-name'>
                                {group.name}
                            </div>
                            <div className='single-card-top-location'>
                                {group.city}, {group.state}
                            </div>
                            <div className='single-card-top-privacy-status'>
                                {uniqueEvents.length} events &middot;  {group.private ? 'Private' : 'Public'}
                            </div>
                            <div className='single-card-top-organizer-fullname'>
                                Organized by: {group.Organizer?.firstName} {group.Organizer?.lastName}
                            </div>
                            <div className='single-card-top-buttons'>
                                {buttons}
                            </div>
                        </div>
                    </div>
                    <div className='single-event-body-container'>
                        <div className='single-group-body-container'>
                            <div className='single-card-bottom-top-container'>
                                <div className='single-card-top-name'>Organizer</div>
                                <div className='single-card-top-location'> {group.Organizer?.firstName} </div>
                            </div>
                            <div className='single-card-bottom-mid-container'>
                                <div className='single-card-top-name'>What we're about</div>
                                <div className='single-card-about'>{group.about}</div>
                            </div>

                            <div className='single-card-events-container'> 
                                <div className='single-card-top-name'> 
                                    Upcoming Events({futureEventsCount})
                                </div>
                                
                                {loading ? (
                                    <div className='loading-notification'>Loading events...</div>
                                ): uniqueEvents.length === 0 ? (
                                    <div className='no-upcoming-events-text'>No upcoming events</div>
                                ) : (
                                    uniqueEvents.map(event => (
                                        <SingleEventCard key={event.id} event={event} />
                                    ))
                                )}
                            </div>

                            <div className='single-card-events-container'>
                                <div className='single-card-top-name'>
                                    Past Events({uniquePastEventsCount})
                                </div>
                                <div>
                                {loading ? (
                                    <div className='loading-notification'>Loading events...</div>
                                ): uniquePastEvents.length === 0 ? (
                                    <div className='no-upcoming-events-text'>No upcoming events</div>
                                ) : (
                                    uniquePastEvents.map(event => (
                                        <SingleEventCard key={event.id} event={event} />
                                    ))
                                )}
                                </div>
                                
                            </div>

                        </div>
                    </div>
                </div>
            
            </div>)}

Contact

Chris Gomez: Andre89Gomez@gmail.com

About

MeetPup is an inspired clone of the site: Meet-UP. This project is an effort to display the work for the utilization of React, Redux, Express, Sequelize, PostgreSQL, and Render.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published