Git Product home page Git Product logo

unwined's Introduction

Unwined

Unwined is a wine rating App that assists users to discover and rate new wines. It is a fullstack React App made with a Redux state manager and a backend using Node/Express, Sequelize, and PostgresSQL.

  • View the Unwined App Live

  • It is modeled after the Untappd App

  • There are over 5k wines, 3k wineries and 300 wine types seeded in the database

  • Reference to the Unwined Wiki Docs

Table of Contents
1. Features
2. Installation
3. Technical Implementation Details
4. Future Features
5. Contact
6. Special Thanks

Technologies

Features

Splash Page

Unwined logo and features of site are shown Splash Page

Sign In and Sign Up

Sign Up Sign In

Wine Detail

Single wine details of name, wine type, price, review, etc. Wine Detail

Edit Wine

Edit a wine in the database Edit Wine

Review

Users can add reviews for a wine Review

Review In Edit and Delete

Edited Review is highlighted in blue with options to save or cancel changes Review Edit Delete

Dashboard

Discover and search for new wines, or add a new one Dashboard

Add Wine

Add a new wine to the database Add Wine

Pagination

Page 7 is highlighted and displays that subset of the wines Pagination

Search Modal

Modal renders search results below as user types input Search Modal

Installation

To build/run project locally, please follow these steps:

  1. Clone this repository
git clone https://github.com/nicopierson/unwined.git
  1. Install npm dependencies for both the /frontend and /backend
npm install
  1. In the /backend directory, create a .env based on the .env.example with proper settings
  2. Setup your PostgreSQL user, password and database and ensure it matches your .env file
  3. Run migrations and seeds in the /backend
npx dotenv sequelize db:create
npx dotenv sequelize db:migrate
npx dotenv sequelize db:seed:all
  1. Start both the backend and frontend
npm start

Technical Implementation Details

CSS Transitions

The first goal was to find a method to dynamically unmount a component from an event e.g. click or an input change. After searching and experimenting, I discovered the CSSTransition component from the react-transition-group package.

First the state is declared and references are made:

const [toggleForm, setToggleForm] = useState(false);
const [ref, setRef] = useState(React.createRef());

useEffect(() => {
  setRef(React.createRef())
}, [toggleForm]);

Then one CSSTransition component holds the WineDetailPage, and another the WineForm as a child. The WineDetailPage unmounts when a user clicks the Edit button, and afterwards the WineForm component mounts. These components will swap again when a user clicks the Cancel button in the WineForm.

Integrating these components with Transition components allow dynamic mounting based on a toggle state such as toggleForm shown below:

return (
    <>
      <CSSTransition
        in={!toggleForm}
        timeout={800}
        classNames='wine_detail'
        nodeRef={ref}
        unmountOnExit
      >
        <>
          <WineDetailPage 
            ref={ref}
            setToggleForm={setToggleForm}
          />
          <CheckIn />
        </>
      </CSSTransition>
      <CSSTransition
        in={toggleForm}
        timeout={800}
        classNames='wine_edit_form'
        unmountOnExit  
        nodeRef={ref}
      >
        <WineForm 
          ref={ref} 
          setToggleForm={setToggleForm}
          method={'PUT'}
          title='Edit Wine'
        />
      </CSSTransition>
    </>
  );

Search

In order to access all of the wines, the search feature was vital. At first, I created separate routes accepting parameter variables to search based on name, rating, and price. Unfortunately, it became messy and hard to read. As a result, I opted to use a query string from the useLocation React hook instead of sending search parameters with useParams hook.

Below the route to query a wine from sequelize, uses the query string to extract variables for the search string, order, attribute, and page number:

router.get(
  '/',
  asyncHandler(async (req, res, next) => {
    let { search, page, attribute, order: orders } = req.query;
    if (!page) page = 1;
    const offset = limitPerPage * (page - 1);

    let { where, order } = createQueryOptions(attribute, orders);

    if (search) {
      where = {
        ...where,
        name: {
          [Op.iLike]: `%${search}%`
        }
      };
    }

    const wines = await Wine.findAndCountAll({
      offset: offset,
      limit: limitPerPage,
      where: where ? where : {},
      order: order ? order : [],
    });

    return res.json({ ...wines, offset })
  })
);

Pagination

It is also excessive to show more than 5k wines on a page, which can lead to excessive overhead and a slower response time.

In response, I created a Pagination component to calculate the offset number per page, and passed as a query string to the api route:

const { search: searchString } = useLocation();
let { attribute, order, search } = queryString.parse(searchString);
if (!attribute) attribute = 'name';
if (!order) order = 'desc'; 

let numberOfPages = Math.ceil(numberOfResults / itemsPerPage);
if (numberOfPages > pageLimit) numberOfPages = pageLimit;

if (!numberOfPages) return null;
const pageNumbers = [...Array(numberOfPages).keys()];

Future Features

  1. Feed Page - show most recent reviews

  2. Favorite - like wines and add to feed page

  3. Friends - add friends and display their reviews on feed page

  4. Wineries - CRUD for wineries

Contact

Special Thanks

unwined's People

Contributors

nicopierson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

unwined's Issues

Add search feature

Search feature should include a search bar with a modal, and a search page of the search results. Sorting the search results by price, rating and country would be useful.

Restyle Favorite

Restyle the favorite component when a wine is added as a favorite

Add feed to dashboard page

When a user logs in, they will be redirected to the dashboard with the most recent reviews about wines.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.