Skip to content

Latest commit

 

History

History
161 lines (127 loc) · 5.41 KB

File metadata and controls

161 lines (127 loc) · 5.41 KB

Super Treats

Super Treats is a solo project build as the capstone project at App Academy, the culmination of 6 months of focused study on fullstack developement. Super Treats is an eCommerce site for food. Hungry users can browse food options and place orders, while business focused users can register their restaurants and menus to create a new source of income. The live site is available @ https://super-treats.onrender.com

For more details on features and application architecture see our wiki:

Tech and Languages

  • React
  • Redux
  • Flask
  • SQLAlchemy
  • Javascript
  • Python
  • Postgres
  • CSS
  • HTML

How to Build

In the root directory:

  pipenv install -r requirements.txt &&
  pipenv shell &&
  cp .env.example .env &&
  flask db upgrade &&
  flask seed all &&
  flask run

In a second terminal:

  cd react-app &&
  npm install &&
  npm start

In your browser, navigate to http://localhost:3000/

Screenshots

image image image image image image image

Future Features

  1. Search and filter for businesses and items
  2. Allow business owners to create multiple menus and split items by category
  3. Connect a map API to show locations and estimate delivery times
  4. Create a system that allows users to customize their orders i.e. portion size and toppings

Code I Found Interesting

Multi-stage Forms

This was my first time implementing a multistage form and I though my solution was pretty neat. Each stage of the form was turned into a separate component, such as SignupFormEmail, that handled any related logic. The main form component, SignupFormPage, kept the state and handled the rendering. The code below shows a simplified version of the relevant code.

/* /react-app/src/components/auth/SignupFormPage/SignupFormPage.js */

import SignupFormEmail from "./SignupFormEmail";

export default function SignupFormPage() {
  const [email, setEmail] = useState(""); 
	const [step, setStep] = useState(0);

	const formSteps = [    
  	SignupFormEmail,
  ];
  
  const nextStep = () => {
    if (step < formSteps.length - 1) 
      setStep((step) => step + 1);
    else handleSubmit();
  };

  const CurrentStep = formSteps[step];

  const formData = {
    email: {email, setEmail},
    steps: { nextStep }
  }

  return (
    <form>
      <CurrentStep formData={formData}/>
    </form>
  )
}
/* /react-app/src/components/auth/SignupFormPage/SignupFormEmail.js */

export default function SignupFormEmail({ formData }) {
  const { email, setEmail } = formData.email;
  const { nextStep } = formData.steps;

  const submitEmail = async (e) => {
    e.preventDefault();
    const res = await dispatch(validateEmail(email)); // check for uniqueness on the backend
    if (!res.errors) nextStep();
    // else handle errors
  }

  return (
    <section>
      <input
        value={email}
        onChange={setEmail}
      />
      <button onClick={submitEmail}>
        Submit Email
      </button> 
    </section>
  )
}

Separating Image and Non-Image Cards

When browsing items in a restaurant's menu, item cards are displayed in a flex container up to 4 in a row for cards with images, and up to 2 in a row without. If there are not enough to fill a row, there is still a separation between images and non-images. image

Each item card is an individual component that does not know about its siblings. The page that they are rendered on only passes in an item id as a prop, so there is no way to split or sort the items. Instead I have a useEffect the utilizes css classes and DOM manipulation to insert a spacer element where required.

/* /react-app/src/components/ItemBrowsingPage/ItemBrowsingPage.js */
  useEffect(() => {
    const firstNonPicElement = document.querySelectorAll(
      ".item-card--image+.item-card--no-image"
    );

    for (let ele of firstNonPicElement) {
      const spacer = document.createElement("div");
      spacer.classList.add("flex-spacer");
      ele.parentNode.insertBefore(spacer, ele);
    }
  });