Nommly
~ Nommly is a Yummly clone. Nommly is a really cool app that at its core allows foodies to share-in their love for food! As a user, you are able to search recipes semantically, curate your feed through setting profile preferences, nommed recipes list and search history.
Configuration
~ run $ npm install
~ run $ bundle install
~ run $ rails db:reset
~ run in one terminal $ npm run webpack
~ run in second teminal $ rails server
Hey Nikki, it's clear you put a lot of thought into drafting your schema and frontend state. My hat's off to you for all your great questions!
The design doc rubric is below, along with my feedback. Please let me know if you have any questions!
Database Schema
Contains correct datatypes
Contains appropriate constraints/details
primary key
not null
unique
indexed
foreign key
Contains bullet points after the table that state which foreign keys will reference to which table, or references to the associations which will be made
foreign key and table name are lowercased, snake_cased and back_ticked
Correctly formatted
schema is written in a table format
the table's name are lowercased, snake_cased and back_ticked
the table header column names are bolded
columns names are lowercased and snaked_cased and back_ticked
Comments
recipes:
Answers to your questions listed below the table:
It looks like the Yummly Developer API is no longer accepting new accounts, and at any rate, I wouldn't recommend using the API of the same app that you're cloning. You should store all the info you want to display in your own database.
You're absolutely right that ratings would be stored only in a separate table (i.e., in the reviews table)
Yes, you'll want separate columns for each of the nutrition macro types. However, to make seeding easier at the beginning of your project, it's totally fine to limit the info to a single calories column for now (you can always add sodium, fat, protein, carbs, fiber columns later)
Remove ingredients column; these need to be stored in a separate table, and you can use a recipe_ingredients joins table to link recipes with their ingredients. (One principle of designing a "normalized" database is that each column should only store one value, not a collection of values. If a recipe can have many ingredients and an ingredient can have many recipes, we should use an intermediate join table to facilitate this many-to-many relationship.)
Change nutrition column to calories (as mentioned in response to your question above).
Add cuisine_id and course_id so that users can search recipes by cuisine or course. (From browsing Yummly, I gather that Yummly only classifies each recipe under a single cuisine and a single course, which means each recipe will only have one associated cuisine_id and course_id.)
Remove icon_url. You should consider eventually storing your recipe images in a cloud service (AWS S3) and managing references to those files via Active Storage... which means you won't need to store the icon_url in the recipes table. I wouldn't worry about AWS until towards the end of the 2-week sprint; until then, you can just use locally-stored placeholder images for your recipes.
Change description and directions to have a text datatype
ingredients and recipe_ingredients
Add two new tables, ingredients and recipe_ingredients
recipe_ingredients can look similar to the design that you proposed for your recipe_tags table (i.e, you need a way to link a recipe with an ingredient)
cuisines and courses
Add two tables to store the standard cuisines and courses that Yummly uses to categorize recipes.
favorites, or user_preferences
Add a favorites or user_preferences table. If the user profile or personalized browse feed is going to be one of your main features, you'll need a place to store the user's preferences. When a user goes through the "personalize your experience" form on Yummly, they're asked to select preferences across many different dimensions: cuisine, allergies, diet, ingredients (to exclude), cooking skills, cooking goals. You should limit it to just two few of those: cuisine, ingredients.
Since a user can customize their preferences for more than one type of thing (i.e., they can have both a set of favorite cuisines and a set of excluded ingredients), you should consider making this a polymorphic joins table.
The ArtShare API project (W6D3&4) provides an example of how to set up polymorphic associations both in your schema and in your models.
In addition to user_id, preferable_id and preferable_type columns, you may want an additional column to indicate whether the specific preference represents something to include or exclude. (E.g., include the selected cuisines but exclude the selected ingredients)
Bonus tables
Remove bloggers, recipe_tags, hash_tags, reviews, ,likes, flags, or move them to the very bottom of your schema plan under a "BONUS" section.
Sample State
State shape is flat!
State's keys are camelCased
All keys within the values in the state are accessible in the schema
Correctly formatted
Sample state is rendered with triple backticks, and the language ```javascript...```). This will display the state as a code block instead of a giant line of text
Top level slices
entities
session
errors (here or in ui)
ui (if needed)
Should NOT have nested slices, aka comments inside of posts
Some info from other tables is ok, for instance:
the author username and imageurl for a post. basically any info that the user can't change
like count and a boolean on whether the user likes the post instead of a likes slice
Comments
Nice job normalizing your state!
Make sure any changes you make to the final draft of your schema are reflected in the sample state as well. (e.g., if you add any tables or columns, add corresponding keys to the appropriate slice of state)
aside from formatting / naming conventions, there are two main differences to maintain between your database schema and frontend state:
Tables in a normalized database shouldn't store arrays as values, but it's ok to do that in your frontend state, as long as those arrays store ids. You have the right idea with the recipesIds key in your users slice of frontend state. However, conventionally, the keys that store an array of ids should be named as singularResourceIds, e.g., recipeIds and reviewIds.
When it comes time to build the recipes feature, you can think about how to send an array of recipeIds back with every request for user info. At that point, you might want to look into ActiveRecord's collection_singular_ids method, which you'll be able to use in your jbuilder. Assuming you define a has_many :saved_recipes association in your User model, Rails will automatically give you a #saved_recipe_ids instance method that generates an array of ids for all the saved_recipes associated with a user.
Even though you don't need an icon_url column in your database tables, you'll want to keep the iconUrl key in your frontend state. This will be generated via an association method that you define in your models. (If this doesn't make sense now, that's ok. It will make more sense after you go through the AWS S3 tutorial much later in your project.)
To keep state normalized, the ingredients key in your recipes slice of state should point to an array of ingredientIds, not an array of actual ingredient names. The reason this matters is b/c you don't want ingredients to be listed in more than one place. (There should be a single source of truth for every piece of information.) All your ingredients should instead be listed in a separate ingredients slice of state under entities
Wonderful work on User Auth, Nikki! I have a few comments below, but these can be addressed after Friday. Let me know if you have questions!
User Auth
Functionality
The main button with logo should link to /
Has working demo login
Smooth, bug free navigation
Before Login
The /login page should not display a link to Log In. Same for /signup
Going to a random route /#/oweiniouewbrviuwebv should redirect or display a 404 page
Errors should display for both /signup and /login.
Errors display beautifully for the email page and the login-password form!
However, for the signup-password form, if I only fill out the password and leave out the user name, I'm taken to a blank white page and there are errors in the console: this.props.errors.map is not a function. This is usually a sign that there was some 500-level error thrown on the back end, and your frontend code (errors reducer and React component) can't handle the 500-level error. As with any 500-level server error, check your server log for more info.
Errors should clear when moving between /signup and /login.
Can sign up a user
Can sign in as a user
Can log out a user
Can't sign up with the same username/email
Pressing enter after filling out the session form should use the form data, not the demo user
After Login
Should not be able to visit any of the frontend urls used for signing up or logging in: /email or /login-password or email-password
You can use your route_utils to achieve this behavior; these should all be AuthRoutes
Should be able to refresh the page and still be logged in
Style
The site should look exactly like the actual site
Styling is magnificent - really on point! I know we talked about how Yummly's signup process is a little weird and doesn't ask for a password right away; it's totally ok to deviate from that and the fix you chose (simulating the two-page signup process and adding the password field to the "How should we call you page") is perfect
All relevant elements should have cursor: pointer on hover
When errors are displayed, most elements should not move around the page (particularly input boxes)
Hi Nikki, I love your app choice and name - it looks like it will be a fun app to clone with lots of cool features to work on. Kudos on the hard work you put into your design docs as well!
We'll meet 1:1 today to 1) go over any questions you have about my feedback or the fullstack workflow, 2) iron out the MVP list, 3) discuss any suggested prep for over the weekend.
Below are my comments on your MVP list. I'll be sure to provide more detailed feedback on the rest of your design documents during our meeting and also in written form by tomorrow morning.
Wiki Page Home
Is the first page you see upon entering the wiki
Contains a welcome message
Contains a link/placeholder for a link to the live page
All links in the right sidebar should contain each wiki page and link to the correct page
Correctly formatted
each wiki page is listed in bullet points
all links route the correct page
Comments
Looks good! Just need to add a placeholder for a link to the live page (which you'll ultimately update with actual link when you push your app to production for the first time on Monday evening)
MVP List
Should have 7 MVPs.
3 of those are User Auth, Heroku, and Production README.
The other 4 are from the MVP List or they have clarified them with you
Contains a description sentence of the app
Includes two to three detailed bullets on functionality and presentation of feature
At least one CRUD feature, which states what CRUD operations are planned (creation, reading, updating, deletion)
Estimates how long it will take the code each MVP
Correctly formatted
MVPs are listed in an ordered list
Each MVP is broken down into bullet points
Comments
These are some really cool features you've listed out, but it's more than can fit into the initial 10-day fullstack project sprint. Narrow down the list to 7 core MVPs (4 besides user auth, hosting on Heroku, and production README). Indicate which MVPs will be your bonuses. Finally, some of the listed MVPs can be further broken down into smaller and more "atomic" features. (Each MVP should focus on one main piece of functionality.) More specific recommendations below ...
I recommend making Recipes your first feature after user authentication. Even though this is a read-only feature (users can browse recipes but can't create / update / delete them), this is a significant step in getting your app set up. Users first need to be able to view recipes before they can search for them, filter them, or save / favorite them.
This may take a bit more work than you'd expect.
First: the seeding will be important. You'll need to select a variety of recipes (I suggest 10 to start, and more after the initial 10-day sprint).
You'll need to setup an ingredients table and seed it with all the ingredients that are in the recipes that you choose to seed your DB with.
You also need to decide whether to store links to external recipes or internal recipes ... or both, though I strongly recommend sticking to just one to begin with (and linking to external recipes is easier b/c then you won't need to store recipe_steps in your database).
Yummly's value proposition seems to center around 3 core pieces of functionality: semantic search, recipe storage (saving your favorites), personalized recipe recommendations. For this reason, I recommend you focus the remaining 3 MVPS on: Favorites, Search, and a Browse Feed. I'd love to hear your thoughts - we can discuss more during our 1:1.
User authentication:
remove the 3rd bullet point ("Logged-in user (user) can opt for a subscription or 30-day trial"); we don't typically include account/subscription tiers as part of the minimum viable product. (However, you could definitely re-purpose the free trial component / button for your demo login)
Don't forget to add time estimates to each of your main features (remember that you'll have a whole half-day to start User auth on Monday)