Well, this post has taken a bit longer than I would have liked, but I’ve been pretty busy so hopefully that’ll make up for it.
It was time for the list page to get a bit more love. The last time I worked on it was at the very beginning of this project, and it was little more than a quick sweep over to get the layout down. Well, I’m going to be doing some changes to the layout in the future (stay tuned for that blog post), but in the mean time I wanted to add some new functions: namely, the ability to add and remove recipes from the list, and the ability to add additional ingredients.
Both of these proved more complicated than I expected.
The Quick Recipe Bar
I wanted a way for a user to quickly add and remove recipes from their grocery list without having to toggle back to the main page and do it there (not that they can currently do it there either… man, there’s still a lot to do on this project). My idea was to have a sidebar that would list all of the user’s recipes, with a single button to choose if they wanted the recipe in or out of the list. This toolbar would have to load the recipes itself, check if they were in the list, and then conditionally render a button to add or remove them. The list would then need to be refreshed.
After some work, here’s what I came up with. Each recipe is stored on a RecipeSideSelector component, which is basically a card with some styling.
It has functions to both add and remove a recipe from a list, depending on the recipe’s current state. It does so by making the necessary funciton calls (notice I have switched to using axios, an ongoing process).
The appearance and presence of these cards is governed by the QuickRecipeAdd component, which gathers the recipes and determines if they are a part of the list or not. It’s render method makes use of the Drawer component from Material UI, like so:
Whenever the drawer is opened, a useEffect hook calls the recipes and the associations:
The (still buggy) recipeIsAssociated method determines if an association is in the recipe or not, depending on the provided associations state.
Finally, there’s a small method (passed down to the RecipeSideSelector component) that essentially refreshes the list of ingredients whenever a recipe is added or removed.
And that pretty much sums up the quick recipe toolbar. As I mentioned above, there are some bugs that still need fixing, but my next cycle is a bunch of polishing and bug fixing, so I’ll take care of those things then.
Additional Ingredients
This one was significantly harder, and required some modifying of the backend before I could get it to work.
First, I created a few new components, all variants of a single AddIngredient component. There was the parent component and then two children, one for when the component was “open” and one for when it was “closed.”
The ClosedAddIngredient is the simpler of the two: it’s just a button with a “plus” on it that, when clicked, opens the OpenAddIngredient component.
The OpenAddIngredient component is a bit more complex, and a bit of it won’t make sense until I explain the backend changes, so keep that in mind. It first calls the new route “/lists/[listId]/additionalingredinets” to retrieve the new, dedicated “Additional Ingredients” recipe. Then, it defines a function that will add an ingredient to that recipe, saving most of the formatting work for the backend.
The actual component returns a Box, an Input, and two buttons, one to submit the ingredient and one to close the component.
On the backend side of things, I first made a few more modifications to my underlying model, adding a new, dedicated Recipe for storing additional ingredients to every list.
The create_additional_ingredients function is called during the creation of every new list:
I also added a new route for accessing this list:
I then made some changes to the RecipeLine schema, such that it would check for the tag “additional_ingredients”. If it found it, it would run the line through the spaCy parser for an accurate token split, and then set the entire line as the ingredient.
This is a workaround, and it comes from the fact that there isn’t really an easy way to directly associate an ingredient with a list. The creation of a middle-man recipe is the best way I can think of doing this without drastic changes to the underlying model structure. And I think it has some advantages, too; it eliminates the possibility of duplicate ingredients, since anything the user adds that’s already in the list won’t be added a second time. It took a bit of time to make this work though; some of my earlier modifications to make the recipe page work got in the way. I think that’s a sign that things are starting to get complex and I need to take a step back and do some refactoring. I’ve decided to release my first version soon, with a limited amount of features, just to have something out in the world while I continue to work (and continue to look for a job). So I expect my next few posts will involve polishing features and fixing bugs, as well as some under-the-hood refactoring work. Stay tuned.