Skip to content

itsoliviasparks/predict-a-buy

 
 

Repository files navigation

PREDICT-A-BUY 🍋

🔗 Live Site

About

Introducing PREDICT-A-BUY: The Ultimate Shopping Companion!

With PREDICT-A-Buy by your side, shopping becomes an adventure, a journey through time. Let the app be your trusty sidekick, anticipating your needs & guiding you through the aisles with its charming personality. Say goodbye to mundane shopping lists and hello to a shopping experience that is as fun as it is smart!

Project Purpose & Goal

Over an 8-week period in early 2023 I collaborated with three other early-career developers as part of The Collab Lab. Together we built a smart shopping list app in React.js. Our team worked under the guidance of three mentors who are all professionals in the field. They acted as our project managers and led learning modules on collaborative development strategies.

As a team, we focused on pair programming, writing great pull/merge request messages, over-communicating in Slack, demos, retros, and other real-world activities of professional software teams.

Read more about my time at The Collab Lab

Tech Stack

React, Firebase, Sass, & Figma

Use

  • On app mount, the user is prompted to join a shopping list
    • They have 2 options:
    1. They can enter a token to join an existing list, perhaps one they share with family or roommates
    2. They can click to start a new list
  • From there, the user adds items (e.g. "vegan cheeze" or "lemons") to their list
    • When adding an item the user selects when they suspect they will need to buy the item again: soon, kinda soon, or not soon
  • After navigating to their shopping list, the user can see a list of item they need to buy
    • Each item is labeled with how soon they will need to buy it again
    • Each item has a delete button to delete the item from their list
    • The user can check & uncheck items off as they purchase them
    • The user can also search through their list
  • PREDICT-A-BUY is smart:
    • Over time, the app comes to understand the intervals at which you buy different items
    • If an item is likely to be due to be bought soon, it rises to the top of the shopping list
    • If 60 days or more have passed since the last purchase date, it will be labeled as inactive
    • After 24 hours, all purchased items will be marked as un-purchased & labeled with their updated next purchase date prediction

Demo

Screen.Recording.2023-05-27.at.19.00.04.mov

User Interface

The UI design started with a brainstorming session with the entire team. Collectively, we decided on a theme of retro-futurism, blending nostalgic elements with futuristic design concepts to create a captivating & playful user experience. Lizzy, one of our team members, came up with the app's name, which perfectly encapsulated the apps purpose and pur playful vision for the design.

From there the team nominated me to spearhead the UI development for our app, which I was thrilled to do! It was an exciting opportunity for me to showcase my skills and dive into Figma!

Figma was a new tool for me, however, I quickly became familiarized with it and created the app's wireframe & SVG elements. You can check the wireframe out here.

Originally, we had planned to use Material UI for our design framework, but after careful consideration, we decided to switch to Sass to have greater control over the visuals. This decision allowed us to customize the styles and tailor them to our retro-futurist vision.

Given my experience with Sass, I took the lead in sharing my knowledge with the team, helping & supporting my teammates who were new to Sass, ensuring that everyone understood how to utilize its features effectively.

To ensure visual consistency throughout the app, I created core partials for essential components such as color variables, typography, a wrapper, and buttons. Each team member was responsible for developing a different view, and these core partials helped maintain a cohesive design language.

It was also important for us to implement both a dark and light mode into the app's UI design. By incorporating this feature, the app seamlessly toggles between the two modes based on the user's computer settings via the color-scheme meta tag, ensuring optimal readability and visual comfort for every user.

Overall, spearheading the UI development was a rewarding experience. I had a great time stretching my design muscles, collaborating with the team, & creating a visually cohesive user interface that aligned with our app's concept and provided a playful user experience.

Problems Solved

The Problem

When developing this app, I accidentally marked an item as purchased. I immediately knew that I wanted to implement an uncheck feature, even though it wasn't part of the project's Acceptance Criteria or the original scope. I am incredibly grateful for my fellow contributors and our mentors for their patience & support while I worked through this stretch goal.

Initially, this was easy: when the user unchecked an item, we updated the database to reduce the totalPurchases by 1 & reset the dateLastPurchased to null. But we soon realized that this was not a good solution, because the dateLastPurchased value is vital in predicting the next purchase date.

I knew we needed to store the "smart" values of dateNextPurchased & dateLastPurchased to be able to allow the user to uncheck an item and not loose the app's ability to predict when they would next need the item.

The firebase data structure was set up for us by The Collab Lab, and was integral to all the logic we already built into the app-- we could not change it to hold a secondary values for dateNextPurchased & dateLastPurchased. So, I knew we had to use React state.

The Solution

I'm really proud of the logic we developed for this feature:

  1. All list items have a boolean of wasPurchased. This is true if the item's dateLastPurchased is within 24hrs of the current time. This value is derived from state.

    This logic is located in /src/components/ListItem.jsx

        const currentDate = new Date().getTime();
        const dateLastPurchasedPlus24h = dateLastPurchased
            ? dateLastPurchased.toDate().getTime() + ONE_DAY_IN_MILLISECONDS
            : null;
    
        const wasPurchased = currentDate < dateLastPurchasedPlus24h;
    
  2. When the user marks an item as wasPurchased, before we update the database, we save the values of dateNextPurchased & dateLastPurchased into state.

  3. Once we have prevDateNextPurchased & prevDateLastPurchased saved into state, we then trigger the database to update.

    This logic is located in /src/components/ListItem.jsx

        const handleCheck = () => {
            //save previous dateLastPurchased & dateNextPurchased into state to use if the user unchecks an item
            setPrevDateLastPurchased(dateLastPurchased);
            setPrevDateNextPurchased(dateNextPurchased);
    
            updateItem(
                wasPurchased,
                listToken,
                itemId,
                prevDateLastPurchased,
                prevDateNextPurchased,
            );
        };
    
  4. Updating the database is conditional on whether or not the item wasPurchased.

    This logic is located in /src/api/firebase.js

    await updateDoc(listItemRef, {
            // when the user marks an item as purchased, the date is updated to today & 1 is added to number of purchases
            // when the user unchecks an item to mark it as not purchased, the date is updated to the previous purchased date & 1 is subtracted from number of purchases
            dateLastPurchased: wasPurchased ? prevDateLastPurchased : new Date(),
            dateNextPurchased: wasPurchased
                ? prevDateNextPurchased
                : getFutureDate(estimateOfDate),
            totalPurchases: wasPurchased ? totalPurchases - 1 : totalPurchases + 1,
        });
    
  5. Now this poses a problem, since the crucial values of prevDateLastPurchased & prevDateNextPurchased are only saved into state when the user first clicks on an item. Therefore, if an item is marked wasPurchased on app mount, those values are null & the database would not be correctly updated when un-clicked. To solve this, we decided to disable the checkbox for any items that are marked as wasPurchased on app mount.

    This logic is located in /src/components/ListItem.jsx

        //disables ability to uncheck item if the item was marked as purchased on page load
        useEffect(() => {
            if (!prevDateLastPurchased && !prevDateNextPurchased) {
                setDisabled(wasPurchased);
            }
        }, [wasPurchased, prevDateNextPurchased, prevDateLastPurchased]);
    
    

Overall we feel this provides an excellent user experience:

  • The user can uncheck any items they accidentally mark as purchased while using the app
  • It is safe to assume that any items marked as purchased by the user at the end of their session are genuinely purchased
  • Therefore, when the user returns to the app within 24 hours, the item will be crossed out, and the checkbox will be disabled
  • The app's "smartness" is never compromised
  • All items are automatically marked as un-purchased after 24 hours from their last purchase date

Lessons Learned

One of the most significant lessons I learned was the importance of clear communication both in & out of the text editor.

Dedicating time each week to go through the other pair's code, line-by-line, as I reviewed each pull request highlighted the importance of writing maintainable & easy to understand code. Additionally, having our team's fabulous mentors review the pull requests I worked on was incredibly valuable. Their thoughtful comments really opened my eyes to new ways of working through logic & they have undoubtedly made me a better programmer.

Collaborating with other developers was also a great learning experience. I worked with self-taught developers, those from different bootcamps, and professionals in the field. This has helped me expand my knowledge of the various coding techniques and approaches.

Contributors:

Mentors:

About

PREDICT-A-BUY makes shopping a journey through time by anticipating your needs while guiding you through the aisles!

Topics

Resources

Stars

Watchers

Forks

Languages

  • JavaScript 52.9%
  • SCSS 33.8%
  • HTML 13.2%
  • Shell 0.1%