Places lets you rate and rank your favourite places to eat, drink, hang out and more. By joining trusted groups of friends or family and choosing your own criteria for ratings, you can get reliable recommendations about where to go based on whatโs most important to you.
๐ View Demo ยป
Places lets you rate and rank your favourite places to eat, drink, hang out and more. By joining trusted groups of friends or family and choosing your own criteria for ratings, you can get reliable recommendations about where to go based on whatโs most important to you.
As with most modern web applications, our has little to no literal HTML, but having an in-depth understanding of HTML allowed us to work easily with JSX (a JavaScript syntax extension, based on HTML markup conventions, used to arrange and render React components) to structure our front-end elements.
While we generally used the Ant Design framework in lieu of explicit styling to ensure visual consistency, sometimes we still needed to use dedicated CSS files for components to directly control appearance, positioning or other properties of DOM elements.
The logic of our application was built in JavaScript from top to bottom, from its React front-end components, to its integrated Node packages, to its Express-based back-end server. We made extensive use of ES6 features such as arrow functions, rest parameters and array methods.
We used React to create modular UI components and render dynamic views that changed according to user input. We took advantage of React Hooks to handle state and life-cycle logic for each component. We used a Redux store to maintain data such as reviews, places and categories on the front end to ensure consistency and accessibility throughout complex components.
Our back end server, implemented using Node and Express, let us hide critical business logic from users while allowing us to establish secure connections to external data sources such as our MongoDB database and the Cloudinary third-party API for image upload functionality. Node is popular and well-supported, it allowed us to use Javascript throughout the application, and it provides many useful packages. Express was a straightforward framework with which we created a RESTful API to perform our application's C.R.U.D. operations on user, group, category, place and review data.
Entities such as users, places, categories and reviews are stored as MongoDB collections, with each item its own document. While we are using a non-relational database, we kept the data stored in each collection as lean as possible to reduce redundancy โ if something is already recorded in another collection, we donโt duplicate it elsewhere. Our Express-based back end uses Mongoose to ensure data being added to each collection conforms to the expected schema, and to ease communication with the database itself.
To structure and manage our code, we adopted a simple monorepo structure on GitHub where we could take advantage of version control for code history and sharing, pull requests for code review, and GitHub actions for automated deployment, linting and testing. Later, we implemented CI/CD with GitHub actions (triggered by pushes/pull requests on the main branch) and Heroku to build, deploy and host our application. These release engineering efforts, along with our cloud hosted MonogDB on Atlas, made it easier for us to focus our attention on completing requirements, implementing new features and fixing bugs.
In the later stages of design we realized users would want to view, add and review places while they were out and about, and as a result we refactored all views in the application to be as functional on mobile as they are on desktop.
We had been using Ant Designโs framework on our front end to keep design elements like buttons and dividers visually consistent. We refactored all views and components to use Ant Design's implementation of Bootstrap breakpoints for different screen sizes to ensure spacing, sizing and positioning of all elements would create a usable experience on any device. Chromeโs developer tools were also of great use to emulate different mobile screens and debug CSS issues during this process.
We used this multi-platform ethos for other design decisions too: no essential information is ever shown as tooltips, which are difficult to access on mobile; and the AddCategory component, where a user can select a custom emoji, contains a popup emoji picker for desktop users who donโt have an emoji keyboard at the ready.
Our application relies heavily on images to create an engaging user experience. From early on, we knew we wanted a way for users to add images of their choice to their profiles, groups and places. However, as images are so much larger than the strings and numbers being stored in our MongoDB database documents, it was not feasible to store images within our regular database collections. We initially had users input external image URLs, which meant they couldn't use their own pictures and would need to leave our application to find externally hosted files.
We recognized the poor experience this was creating for our users (our friends tested our app), and set out to add upload-your-own-image functionality. We researched and compared many ways of implementing this feature, and chose the most cost effective (free!) and least technically challenging method. We added an image upload field to our front-end forms, and included the userโs chosen image file in the form data sent to our back end, where we forwarded the image to an external API (Cloudinary). Cloudinary saves the image and responds back with an image URL that can be easily stored in our MongoDB as text; this URL can later be fetched when our interface needs to display the image. This allows users to easily upload profile pictures, customize their group logos and add pictures of the places they visit.
The unique features that set our application apart from its big brothers like Yelp and Google Maps are its private groups and group-specific rankings: rather than using vague and unreliable scores from strangers, Places' place rankings are determined by people you know with tastes you trust. In order to keep groups private, we had to ensure user authentication was secure. Authentication in our application would be used to identify the user, give them access to specific groups, remember who they are on the client side, and give them access to our back-end API.
Initially, we had a very weak form of authentication (if you could call it that) where we stored the user's password in plain text in our database, using local storage to simply store a user ID to remember them across sessions. Our API was completely open; you could pretend to be whoever you wanted and access all of our data by making requests without any authorization.
To the front-end user, everything seemed functional. We could have left it at that, but we knew, fundamentally something critical to our application was โbrokenโ. After a lot of research on authentication, we first got rid of all the plain text passwords and implemented industry standards of salting/hashing passwords using bcrpyt. To prevent the users from easily modifying their local storage to pretend to be another user, we implemented JSON Web Tokens. These tokens are signed by our back end after successful login and any tampering with them renders them useless. Then, we also locked down all of our private API endpoints to require an authorization header in the form of a JWT bearer token preventing all non-authorized users from fetching any user, place or review data. The back end also uses the auth header token to identify the user making the request to prevent anyone from leaving reviews as another user or letting people into groups where they are not a member.
Who is it for?
What will it do?
What type of data will it store?
What will users be able to do with this data?
What is some additional functionality you can add/remove based on time constraints?
โ Register new account or log in to existing account
โ User should be able to leave ratings
Groups Screen (after login)
Reviews in a group (after clicking a joined group)
Adding a review to a place (after clicking add review on a place)
You should have Node.js, Yarn and Git installed on your PC.
Clone the repo using:
git clone https://github.com/sassansh/Places.git
In the root of the project /Places/
, create a .env
file and add these environment variables:
PLACES_DB_URI=
PORT=
JWT_SECRET=
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
To start frontend & backend in dev mode together, run:
yarn first-install
yarn dev
Automatic deployments are setup with Heroku each time a push is made to the main
branch.
Access the current live app at:
https://places-cpsc455.herokuapp.com/
Distributed under the MIT License. See LICENSE
for more information.