Skip to content

Unwined is a platform for wine enthusiasts. Find the perfect wine whether you're just getting started or if you are a wine enthusiast.

Notifications You must be signed in to change notification settings

nicopierson/unwined

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

Unwined is a platform for wine enthusiasts. Find the perfect wine whether you're just getting started or if you are a wine enthusiast.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages