From 42b0c2e978a00712bf3358d62d0cd293670497dc Mon Sep 17 00:00:00 2001 From: Ryan Noelk Date: Wed, 22 Nov 2017 16:55:36 -0500 Subject: [PATCH 1/2] Dev to Master (#282) * Feature/redux (#274) * adding redux packages * saving * hacking away * getting loading to work * saving * saving * updating lists * updating readability * saving * updating prop types * fixing up list footer container * fixing up list header container * using redux for store in navbar * cleaning up list item * cleaning up list item * remove state when calling init * adding save * moving around some actions * fixing some things * saving * saving * saving * renaming * fixing up reducers * moving data loading to containers * using promises * adding proper filters * updating components * removing old lists and udpating tests * removing bound item * adding error form * updating prop types * adding error reducer * updating footer height * removing my lists from mobile layout * fixing error constants * adding loading * store items in lists * removing immutable * redirect if the route is bad * removing unneeded code * removing unneeded code * updating gitignore * adding id to load when selcting from the menu * fixing items being wipped on list reload * Feature/node upgrade (#267) * updating 3rd party packages * using const and removing debug * removing react.craeteclass and react.proptypes * adding back public path * adding auth check to each module * upgrading react router * upgrading to react 16 * adding some comments * package updates * Feature/redux recipe (#271) * saing * updating components * splitting the components up more * else if * pass a round function to do dynmaic serving * cleaning components * adding connecting component * adding prop types * fixing tests * adding more tests * adding more tests * adding more tests and some test data * more tests! * make sure the count is updated when adding items to a list * saving * updating spacing the ings * adding it to a good spot * adding working checkboxes * adding server connection * adding a spinner for loading * adding logic for a spinner * saving * saving * saving * adding ing reducers * moving buttoms to there own class * updating tests * updating prop types * updating translations * cleaning up actions * fixing version issues * fixing Ui tests again * clear the checkboxes after adding to a list * Feature/recicpe servings redux (#273) * adding servings to the redux store * updating the reducers maintain all the store * adding clear servings button * updating tests * cleaning up the ings reducers * more clean up for reducers * append -> push * hide the add button when the user in not logged it * Fixing an issue with adding null quantity or measurements (#276) * Feature/pretty checkboxes (#278) * adding better looking checkboxes to the recipe page * fixing more issues with firefox * fixing tests * updating docs and env file (#281) --- api/base/requirements.txt | 1 + api/v1/list/schema.py | 1 + docs/Running_without_Docker.md | 2 + env_dev.list | 1 - env_stg.list | 2 +- frontend/.babelrc | 1 + frontend/Dockerfile | 2 +- .../createComponentWithIntlAndRouter.js | 16 + .../jest_mocks/createComponentWithRouter.js | 13 + frontend/locale/de.json | 21 +- frontend/locale/en.json | 21 +- frontend/locale/es.json | 21 +- frontend/modules/account/components/Alert.js | 30 + frontend/modules/account/components/Login.js | 64 +- frontend/modules/account/stores/AuthStore.js | 6 +- frontend/modules/base/components/404.js | 36 - frontend/modules/base/components/App.js | 14 - frontend/modules/base/components/Footer.js | 4 - frontend/modules/base/components/Loading.js | 20 + frontend/modules/base/components/NotFound.js | 34 + frontend/modules/base/css/core.css | 2 +- .../modules/browse/actions/BrowseActions.js | 5 +- frontend/modules/browse/components/Browse.js | 30 +- .../modules/browse/components/ListRecipes.js | 2 +- frontend/modules/browse/stores/BrowseStore.js | 4 - .../modules/browse/stores/FilterStores.js | 5 - frontend/modules/common/authCheckRedirect.js | 10 + .../common/bindIndexToActionCreators.js | 13 + .../modules/common/form/FormComponents.js | 20 +- frontend/modules/common/history.js | 5 + frontend/modules/common/reducer.js | 10 + .../header/components/GroceryListMenuItem.js | 2 +- frontend/modules/header/components/Nav.js | 13 +- frontend/modules/header/containers/NavBar.js | 35 + frontend/modules/index.js | 99 +- frontend/modules/list/actions/ItemActions.js | 198 +- frontend/modules/list/actions/ListActions.js | 76 +- frontend/modules/list/components/AddItem.js | 65 + frontend/modules/list/components/Error.js | 32 + .../modules/list/components/GroceryList.js | 111 +- .../modules/list/components/ListContainer.js | 192 - .../modules/list/components/ListFooter.js | 170 +- .../modules/list/components/ListHeader.js | 87 +- frontend/modules/list/components/ListItem.js | 98 +- frontend/modules/list/components/ListItems.js | 121 + frontend/modules/list/components/MyLists.js | 121 +- frontend/modules/list/components/NewList.js | 47 +- .../modules/list/constants/ErrorConstants.js | 4 + .../modules/list/constants/ItemConstants.js | 21 +- .../modules/list/constants/ListConstants.js | 16 +- frontend/modules/list/containers/Items.js | 70 + frontend/modules/list/containers/List.js | 60 + frontend/modules/list/css/_error.scss | 4 + frontend/modules/list/css/_list.scss | 4 + frontend/modules/list/css/grocery_list.scss | 1 + .../modules/list/reducers/ErrorReducer.js | 14 + .../list/reducers/GroceryListReducer.js | 10 + frontend/modules/list/reducers/ItemReducer.js | 46 + frontend/modules/list/reducers/ListReducer.js | 39 + frontend/modules/list/stores/ItemStore.js | 143 - frontend/modules/list/stores/ListStore.js | 123 - frontend/modules/list/tests/Error.test.js | 11 + .../modules/list/tests/Ingredients.test.js | 21 + .../modules/list/tests/ListFooter.test.js | 57 + .../tests/__snapshots__/Error.test.js.snap | 22 + .../__snapshots__/Ingredients.test.js.snap | 92 + .../__snapshots__/ListFooter.test.js.snap | 154 + frontend/modules/list/tests/itemData.js | 1 + frontend/modules/list/tests/listData.js | 1 + frontend/modules/news/components/News.js | 2 +- .../modules/recipe/actions/RecipeActions.js | 72 + .../recipe/actions/RecipeItemActions.js | 56 + .../modules/recipe/components/Directions.js | 50 +- .../modules/recipe/components/InfoPanel.js | 92 + .../recipe/components/IngredientButtons.js | 91 + .../recipe/components/IngredientGroups.js | 54 +- .../modules/recipe/components/Ingredients.js | 92 +- frontend/modules/recipe/components/Ratings.js | 48 +- frontend/modules/recipe/components/Recipe.js | 87 - .../modules/recipe/components/RecipeFooter.js | 81 + .../modules/recipe/components/RecipeHeader.js | 45 + .../modules/recipe/components/RecipeScheme.js | 269 +- .../modules/recipe/components/RecipeView.js | 24 + .../modules/recipe/components/SubRecipes.js | 82 +- .../recipe/constants/RecipeConstants.js | 14 + .../constants/RecipeListStatusConstants.js | 5 + frontend/modules/recipe/containers/Recipe.js | 93 + frontend/modules/recipe/css/recipe.scss | 87 +- .../recipe/reducers/IngredientReducer.js | 66 + .../reducers/RecipeListStatusReducer.js | 19 + .../modules/recipe/reducers/RecipeReducer.js | 92 + frontend/modules/recipe/reducers/Reducer.js | 10 + .../recipe/reducers/SubRecipeReducer.js | 53 + .../modules/recipe/tests/InfoPanel.test.js | 58 + .../recipe/tests/IngredientButtons.test.js | 74 + .../recipe/tests/IngredientGroups.test.js | 14 + .../modules/recipe/tests/Ingredients.test.js | 3 +- .../modules/recipe/tests/RecipeFooter.test.js | 31 + .../modules/recipe/tests/RecipeHeader.test.js | 37 + .../modules/recipe/tests/SubRecipes.test.js | 14 + .../__snapshots__/InfoPanel.test.js.snap | 151 + .../IngredientButtons.test.js.snap | 372 ++ .../IngredientGroups.test.js.snap | 146 + .../__snapshots__/Ingredients.test.js.snap | 180 + .../__snapshots__/RecipeFooter.test.js.snap | 84 + .../__snapshots__/RecipeHeader.test.js.snap | 134 + .../__snapshots__/SubRecipes.test.js.snap | 84 + frontend/modules/recipe/tests/data.js | 2 +- .../recipe_form/actions/ImportActions.js | 4 +- .../modules/recipe_form/components/Auto.js | 38 +- .../recipe_form/components/DataListItem.js | 2 +- .../recipe_form/components/ImportForm.js | 3 + .../recipe_form/components/RecipeForm.js | 7 +- .../modules/recipe_form/components/TagList.js | 36 +- .../modules/recipe_form/stores/RecipeStore.js | 4 +- .../__snapshots__/Ingredient.test.js.snap | 50 +- frontend/package.json | 52 +- frontend/public/.gitignore | 8 + frontend/public/bundle.js | 39 - frontend/server.js | 10 +- frontend/webpack.config.js | 53 +- frontend/yarn.lock | 3900 +++++++++++------ 122 files changed, 6907 insertions(+), 3064 deletions(-) create mode 100644 frontend/jest_mocks/createComponentWithIntlAndRouter.js create mode 100644 frontend/jest_mocks/createComponentWithRouter.js create mode 100644 frontend/modules/account/components/Alert.js delete mode 100644 frontend/modules/base/components/404.js delete mode 100644 frontend/modules/base/components/App.js create mode 100755 frontend/modules/base/components/Loading.js create mode 100644 frontend/modules/base/components/NotFound.js create mode 100644 frontend/modules/common/authCheckRedirect.js create mode 100644 frontend/modules/common/bindIndexToActionCreators.js create mode 100644 frontend/modules/common/history.js create mode 100644 frontend/modules/common/reducer.js create mode 100644 frontend/modules/header/containers/NavBar.js create mode 100755 frontend/modules/list/components/AddItem.js create mode 100755 frontend/modules/list/components/Error.js delete mode 100755 frontend/modules/list/components/ListContainer.js create mode 100755 frontend/modules/list/components/ListItems.js create mode 100644 frontend/modules/list/constants/ErrorConstants.js create mode 100644 frontend/modules/list/containers/Items.js create mode 100644 frontend/modules/list/containers/List.js create mode 100644 frontend/modules/list/css/_error.scss create mode 100644 frontend/modules/list/reducers/ErrorReducer.js create mode 100644 frontend/modules/list/reducers/GroceryListReducer.js create mode 100644 frontend/modules/list/reducers/ItemReducer.js create mode 100644 frontend/modules/list/reducers/ListReducer.js delete mode 100644 frontend/modules/list/stores/ItemStore.js delete mode 100644 frontend/modules/list/stores/ListStore.js create mode 100644 frontend/modules/list/tests/Error.test.js create mode 100644 frontend/modules/list/tests/Ingredients.test.js create mode 100644 frontend/modules/list/tests/ListFooter.test.js create mode 100644 frontend/modules/list/tests/__snapshots__/Error.test.js.snap create mode 100644 frontend/modules/list/tests/__snapshots__/Ingredients.test.js.snap create mode 100644 frontend/modules/list/tests/__snapshots__/ListFooter.test.js.snap create mode 100644 frontend/modules/list/tests/itemData.js create mode 100644 frontend/modules/list/tests/listData.js create mode 100644 frontend/modules/recipe/actions/RecipeActions.js create mode 100644 frontend/modules/recipe/actions/RecipeItemActions.js create mode 100644 frontend/modules/recipe/components/InfoPanel.js create mode 100644 frontend/modules/recipe/components/IngredientButtons.js delete mode 100644 frontend/modules/recipe/components/Recipe.js create mode 100644 frontend/modules/recipe/components/RecipeFooter.js create mode 100644 frontend/modules/recipe/components/RecipeHeader.js create mode 100644 frontend/modules/recipe/components/RecipeView.js create mode 100644 frontend/modules/recipe/constants/RecipeConstants.js create mode 100644 frontend/modules/recipe/constants/RecipeListStatusConstants.js create mode 100644 frontend/modules/recipe/containers/Recipe.js create mode 100644 frontend/modules/recipe/reducers/IngredientReducer.js create mode 100644 frontend/modules/recipe/reducers/RecipeListStatusReducer.js create mode 100644 frontend/modules/recipe/reducers/RecipeReducer.js create mode 100644 frontend/modules/recipe/reducers/Reducer.js create mode 100644 frontend/modules/recipe/reducers/SubRecipeReducer.js create mode 100644 frontend/modules/recipe/tests/InfoPanel.test.js create mode 100644 frontend/modules/recipe/tests/IngredientButtons.test.js create mode 100644 frontend/modules/recipe/tests/IngredientGroups.test.js create mode 100644 frontend/modules/recipe/tests/RecipeFooter.test.js create mode 100644 frontend/modules/recipe/tests/RecipeHeader.test.js create mode 100644 frontend/modules/recipe/tests/SubRecipes.test.js create mode 100644 frontend/modules/recipe/tests/__snapshots__/InfoPanel.test.js.snap create mode 100644 frontend/modules/recipe/tests/__snapshots__/IngredientButtons.test.js.snap create mode 100644 frontend/modules/recipe/tests/__snapshots__/IngredientGroups.test.js.snap create mode 100644 frontend/modules/recipe/tests/__snapshots__/RecipeFooter.test.js.snap create mode 100644 frontend/modules/recipe/tests/__snapshots__/RecipeHeader.test.js.snap create mode 100644 frontend/modules/recipe/tests/__snapshots__/SubRecipes.test.js.snap create mode 100644 frontend/public/.gitignore delete mode 100644 frontend/public/bundle.js diff --git a/api/base/requirements.txt b/api/base/requirements.txt index 7e4bb917..d8e05d03 100644 --- a/api/base/requirements.txt +++ b/api/base/requirements.txt @@ -19,6 +19,7 @@ pytz==2016.10 # graphene-django==1.3 git+https://github.com/graphql-python/graphene-django.git@2929d0866c800ae6efa8d35bb40548940a70c31f +graphene==1.4.1 git+git://github.com/RyanNoelk/recipe-scraper.git@1.0.7 # Will need these once I start supporting metric mesurments diff --git a/api/v1/list/schema.py b/api/v1/list/schema.py index 51df7fd9..b4680028 100644 --- a/api/v1/list/schema.py +++ b/api/v1/list/schema.py @@ -31,6 +31,7 @@ def get_node(cls, id, context, info): class GroceryItemNode(DjangoObjectType): class Meta: model = GroceryItem + filter_fields = ['slug', 'list__id'] interfaces = (graphene.relay.Node, ) @classmethod diff --git a/docs/Running_without_Docker.md b/docs/Running_without_Docker.md index 7e476dcd..b3b20f4e 100644 --- a/docs/Running_without_Docker.md +++ b/docs/Running_without_Docker.md @@ -1,5 +1,7 @@ ## Running OpenEats +#### Warning! This docs are outdated. The recommended way to install OpenEats is via docker. + * `git clone https://github.com/RyanNoelk/openeats.git` * `cd openeats/api` * `git checkout dev` diff --git a/env_dev.list b/env_dev.list index b46f1a6c..ea22ba3c 100644 --- a/env_dev.list +++ b/env_dev.list @@ -8,7 +8,6 @@ API_PORT=8000 DJANGO_SECRET_KEY=sdfsadfas32e98zsdvhhsnz6udvbksjdhfi4galshjfg DJANGO_SETTINGS_MODULE=base.settings DJANGO_DEBUG=True -ALLOWED_HOST='' # If you are serving content behind an HTTPS proxy, # Set this to `true`. diff --git a/env_stg.list b/env_stg.list index 91bb21af..b6f40ce3 100644 --- a/env_stg.list +++ b/env_stg.list @@ -8,7 +8,7 @@ API_PORT=8000 DJANGO_SECRET_KEY=sdfsadfas32e98zsdvhhsnz6udvbksjdhfi4galshjfg DJANGO_SETTINGS_MODULE=base.settings DJANGO_DEBUG=False -ALLOWED_HOST='' +ALLOWED_HOST=localhost # If you are serving content behind an HTTPS proxy, # Set this to `true`. diff --git a/frontend/.babelrc b/frontend/.babelrc index ec58d223..8b9271cd 100644 --- a/frontend/.babelrc +++ b/frontend/.babelrc @@ -1,6 +1,7 @@ { "presets": [ "es2015", + "stage-0", "react" ], "plugins": [ diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 7827ddc9..255e40c7 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:8.3.0-alpine +FROM node:8.6.0-alpine # Create app directory RUN mkdir /code diff --git a/frontend/jest_mocks/createComponentWithIntlAndRouter.js b/frontend/jest_mocks/createComponentWithIntlAndRouter.js new file mode 100644 index 00000000..2943f54c --- /dev/null +++ b/frontend/jest_mocks/createComponentWithIntlAndRouter.js @@ -0,0 +1,16 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { IntlProvider } from 'react-intl'; +import { MemoryRouter } from 'react-router-dom' + +const createComponentWithIntl = (children, props = { locale: 'en' }) => { + return renderer.create( + + + { children } + + + ); +}; + +export default createComponentWithIntl; \ No newline at end of file diff --git a/frontend/jest_mocks/createComponentWithRouter.js b/frontend/jest_mocks/createComponentWithRouter.js new file mode 100644 index 00000000..e65e77a3 --- /dev/null +++ b/frontend/jest_mocks/createComponentWithRouter.js @@ -0,0 +1,13 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import { MemoryRouter } from 'react-router-dom' + +const createComponentWithRouter = ( children ) => { + return renderer.create( + + { children } + + ); +}; + +export default createComponentWithRouter; \ No newline at end of file diff --git a/frontend/locale/de.json b/frontend/locale/de.json index 8067c476..9b6860e5 100644 --- a/frontend/locale/de.json +++ b/frontend/locale/de.json @@ -1,10 +1,10 @@ { + "login.alert.unable_to_login": "Das Einloggen war nicht erfolgreich!", + "login.alert.confirm": "Bitte stellen Sie sicher, dass Ihr Benutzername und Passwort richtig sind.", "login.please_sign_in": "Bitte einloggen.", "login.username": "Benutzername", "login.password": "Passwort", "login.sign_in": "Einloggen", - "login.alert.unable_to_login": "Das Einloggen war nicht erfolgreich!", - "login.alert.confirm": "Bitte stellen Sie sicher, dass Ihr Benutzername und Passwort richtig sind.", "404.header": "Unsere Köche haben das Rezept in der Testküche versaut, bitte versuchen Sie etwas anderes.", "404.message": "Entschuldigung! Ein 404 Fehler ist aufgetreten, wir können nicht finden wonach Sie suchen.", "footer.credit": "Erstellt mit {link}", @@ -32,14 +32,16 @@ "nav.brand": "OpenEats", "nav.news": "News", "nav.recipes": "Rezepte durchsuchen", + "list.new-input-placeholder": "Was benötigen Sie noch?", + "list.error.message": "Something went wrong!", "grocery_list.my_lists": "Meine Listen", "grocery_list.footer": "Doppelklicken Sie auf einen Eintrag, um diesen zu verändern.", - "list.new-input-placeholder": "Was benötigen Sie noch?", "list.footer.items_left": "{itemCount, plural, =0 {Keine Einträge übrig} one {1 Eintrag übrig} other {{itemCount} Einträge übrig}}", "list.footer.all": "Alle", "list.footer.completed": "Fertig", "list.footer.active": "Offen", "list.footer.clear_completed": "Fertige Einträge löschen", + "list_header.confirm_delete": "Are you sure you want to delete this list?", "my_lists.no_lists": "Keine Listen vorhanden", "my_lists.new_list": "Erstelle eine neue Liste!", "new_list.header": "Erstelle eine neue Liste", @@ -94,14 +96,17 @@ "recipe.create.photo_label": "Foto", "recipe.create.photo_placeholder": "Foto", "recipe.create.submit": "Rezept anlegen", - "recipe.edit_recipe": "Rezept ändern", "recipe.servings": "Portionen", "recipe.prep_time": "Vorbereitungszeit", "recipe.cooking_time": "Kochzeit", - "recipe.ingredients": "Zutaten", - "recipe.directions": "Anweisungen", + "recipe.minutes": "Minuten", + "recipe.recipe_ingredient_button.save": "Add To", + "recipe.recipe_ingredient_button.check_all": "Check All", + "recipe.recipe_ingredient_button.clear": "Clear", "recipe.source": "Quelle", "recipe.created_by": "Erstellt von", "recipe.last_updated": "Letzte Bearbeitung", - "recipe.minutes": "Minuten" -} + "recipe.edit_recipe": "Rezept ändern", + "recipe.ingredients": "Zutaten", + "recipe.directions": "Anweisungen" +} \ No newline at end of file diff --git a/frontend/locale/en.json b/frontend/locale/en.json index 0953fa93..0f023f76 100644 --- a/frontend/locale/en.json +++ b/frontend/locale/en.json @@ -1,10 +1,10 @@ { + "login.alert.unable_to_login": "Unable to login!", + "login.alert.confirm": "Please confirm that the username and password are correct.", "login.please_sign_in": "Please sign in", "login.username": "Username", "login.password": "Password", "login.sign_in": "Sign in", - "login.alert.unable_to_login": "Unable to login!", - "login.alert.confirm": "Please confirm that the username and password are correct.", "404.header": "Our chef's ruined this recipe in the test kitchen, we suggest you try something else.", "404.message": "Sorry the page came back with a 404 error we can't find what you are looking for.", "footer.credit": "Created with {link}", @@ -32,14 +32,16 @@ "nav.brand": "OpenEats", "nav.news": "News", "nav.recipes": "Browse recipes", + "list.new-input-placeholder": "What else do you need?", + "list.error.message": "Something went wrong!", "grocery_list.my_lists": "My Lists", "grocery_list.footer": "Double Click to edit an item.", - "list.new-input-placeholder": "What else do you need?", "list.footer.items_left": "{itemCount, plural, =0 {No items} one {1 item left} other {{itemCount} items left}}", "list.footer.all": "All", "list.footer.completed": "Completed", "list.footer.active": "Active", "list.footer.clear_completed": "Clear completed", + "list_header.confirm_delete": "Are you sure you want to delete this list?", "my_lists.no_lists": "No lists to display", "my_lists.new_list": "Create a new list!", "new_list.header": "Create a new list", @@ -94,14 +96,17 @@ "recipe.create.photo_label": "Photo", "recipe.create.photo_placeholder": "Photo", "recipe.create.submit": "Submit recipe", - "recipe.edit_recipe": "Edit recipe", "recipe.servings": "Servings", "recipe.prep_time": "Prep time", "recipe.cooking_time": "Cooking time", - "recipe.ingredients": "Ingredients", - "recipe.directions": "Directions", + "recipe.minutes": "minutes", + "recipe.recipe_ingredient_button.save": "Add To", + "recipe.recipe_ingredient_button.check_all": "Check All", + "recipe.recipe_ingredient_button.clear": "Clear", "recipe.source": "Source", "recipe.created_by": "Created by", "recipe.last_updated": "Last edit", - "recipe.minutes": "minutes" -} + "recipe.edit_recipe": "Edit recipe", + "recipe.ingredients": "Ingredients", + "recipe.directions": "Directions" +} \ No newline at end of file diff --git a/frontend/locale/es.json b/frontend/locale/es.json index dc4cb2da..465d2e38 100644 --- a/frontend/locale/es.json +++ b/frontend/locale/es.json @@ -1,10 +1,10 @@ { + "login.alert.unable_to_login": "Identificación fallida!", + "login.alert.confirm": "Asegurese de que su nombre de usuario y contraseña sean correctos.", "login.please_sign_in": "Identifiquese por favor", "login.username": "Nombre de usuario", "login.password": "Contraseña", "login.sign_in": "Identifiquese", - "login.alert.unable_to_login": "Identificación fallida!", - "login.alert.confirm": "Asegurese de que su nombre de usuario y contraseña sean correctos.", "404.header": "Nuestro chef no ha podido preparar esta receta, te sugerimos que intentes buscar otra cosa.", "404.message": "No hemos podido encontrar lo que buscas.", "footer.credit": "Creado con {link}", @@ -32,14 +32,16 @@ "nav.brand": "OpenEats", "nav.news": "Noticias", "nav.recipes": "Recetas", + "list.new-input-placeholder": "¿Qué necesito comprar?", + "list.error.message": "Something went wrong!", "grocery_list.my_lists": "Mis listas", "grocery_list.footer": "Haga doble click para modificar ", - "list.new-input-placeholder": "¿Qué necesito comprar?", "list.footer.items_left": "{itemCount, plural, =0 {No quedan productos} one {Queda un producto} other {Quedan {itemCount} productos}}", "list.footer.all": "Todos", "list.footer.completed": "Completados", "list.footer.active": "Pendientes", "list.footer.clear_completed": "Borrar completado", + "list_header.confirm_delete": "Are you sure you want to delete this list?", "my_lists.no_lists": "No hay listas para mostrar", "my_lists.new_list": "Crear una nueva lista", "new_list.header": "Crear una nueva lista", @@ -94,14 +96,17 @@ "recipe.create.photo_label": "Foto", "recipe.create.photo_placeholder": "Foto", "recipe.create.submit": "Guardar receta", - "recipe.edit_recipe": "Modificar receta", "recipe.servings": "Sirve", "recipe.prep_time": "Tiempo de preparación", "recipe.cooking_time": "Tiempo de cocción", - "recipe.ingredients": "Ingredientes", - "recipe.directions": "Instrucciones", + "recipe.minutes": "minutos", + "recipe.recipe_ingredient_button.save": "Add To", + "recipe.recipe_ingredient_button.check_all": "Check All", + "recipe.recipe_ingredient_button.clear": "Clear", "recipe.source": "La Fuento", "recipe.created_by": "Creado por", "recipe.last_updated": "Modificado", - "recipe.minutes": "minutos" -} + "recipe.edit_recipe": "Modificar receta", + "recipe.ingredients": "Ingredientes", + "recipe.directions": "Instrucciones" +} \ No newline at end of file diff --git a/frontend/modules/account/components/Alert.js b/frontend/modules/account/components/Alert.js new file mode 100644 index 00000000..758cd513 --- /dev/null +++ b/frontend/modules/account/components/Alert.js @@ -0,0 +1,30 @@ +import React from 'react' +import { + injectIntl, + defineMessages +} from 'react-intl'; + + +const Alert = ({ intl }) => { + const messages = defineMessages({ + title: { + id: 'login.alert.unable_to_login', + description: 'Fail to login header', + defaultMessage: 'Unable to login!', + }, + message: { + id: 'login.alert.confirm', + description: 'Fail to login message', + defaultMessage: 'Please confirm that the username and password are correct.', + } + }); + + return ( +
+ { intl.formatMessage(messages.title) } + { intl.formatMessage(messages.message) } +
+ ) +}; + +export default injectIntl(Alert) diff --git a/frontend/modules/account/components/Login.js b/frontend/modules/account/components/Login.js index bb50760a..d653de4f 100644 --- a/frontend/modules/account/components/Login.js +++ b/frontend/modules/account/components/Login.js @@ -5,9 +5,10 @@ import { defineMessages, formatMessage } from 'react-intl'; -import { browserHistory } from 'react-router' + import AuthActions from '../actions/AuthActions'; import AuthStore from '../stores/AuthStore'; +import Alert from './Alert' // Load in the base CSS require("./../css/login.scss"); @@ -19,34 +20,36 @@ function getAuthErrors() { }; } -export default injectIntl(React.createClass({ - getInitialState: function() { - return getAuthErrors(); - }, +class Login extends React.Component { + constructor(props) { + super(props); + + this.state = getAuthErrors(); + } - componentDidMount: function() { + componentDidMount() { if (AuthStore.isAuthenticated()) { - browserHistory.push('/'); + this.props.history.push('/'); } AuthStore.addChangeListener(this._onChange); - }, + } - componentWillUnmount: function() { + componentWillUnmount() { AuthStore.removeChangeListener(this._onChange); - }, + } - _onChange: function() { + _onChange = () => { this.setState(getAuthErrors()); - }, + }; - handleSubmit: function(e) { + handleSubmit = e => { e.preventDefault(); - var username = this.refs.username.value; - var pass = this.refs.pass.value; + let username = this.refs.username.value; + let pass = this.refs.pass.value; AuthActions.getToken(username, pass); - }, - render: function() { + }; + render() { const {formatMessage} = this.props.intl; const messages = defineMessages({ please_sign_in: { @@ -81,29 +84,6 @@ export default injectIntl(React.createClass({ ) } -})); - -var Alert = injectIntl(React.createClass({ - render: function() { - - const {formatMessage} = this.props.intl; - const messages = defineMessages({ - title: { - id: 'login.alert.unable_to_login', - description: 'Fail to login header', - defaultMessage: 'Unable to login!', - }, - message: { - id: 'login.alert.confirm', - description: 'Fail to login message', - defaultMessage: 'Please confirm that the username and password are correct.', - } - }); +} - return ( -
- { formatMessage(messages.title) } { formatMessage(messages.message) } -
- ) - } -})); \ No newline at end of file +export default injectIntl(Login) diff --git a/frontend/modules/account/stores/AuthStore.js b/frontend/modules/account/stores/AuthStore.js index a25012cf..5e8f9f15 100644 --- a/frontend/modules/account/stores/AuthStore.js +++ b/frontend/modules/account/stores/AuthStore.js @@ -1,7 +1,7 @@ import AppDispatcher from '../../common/AppDispatcher'; import AuthConstants from '../constants/AuthConstants'; import { EventEmitter } from 'events'; -import { browserHistory } from 'react-router' +import history from '../../common/history' const CHANGE_EVENT = 'change'; @@ -63,7 +63,7 @@ AuthStore.dispatchToken = AppDispatcher.register(action => { case AuthConstants.LOGIN_USER: setUser(action.user); AuthStore.emitChange(); - browserHistory.push('/'); + history.push('/'); break; case AuthConstants.LOGIN_ERROR: @@ -74,7 +74,7 @@ AuthStore.dispatchToken = AppDispatcher.register(action => { case AuthConstants.LOGOUT_USER: removeUser(); AuthStore.emitChange(); - browserHistory.push('/'); + history.push('/'); break; default: diff --git a/frontend/modules/base/components/404.js b/frontend/modules/base/components/404.js deleted file mode 100644 index d0168066..00000000 --- a/frontend/modules/base/components/404.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { - injectIntl, - IntlProvider, - defineMessages, - formatMessage -} from 'react-intl'; - -require("../css/404.scss"); - -export default injectIntl(React.createClass({ - render: function() { - - const {formatMessage} = this.props.intl; - const messages = defineMessages({ - header: { - id: '404.header', - description: '404 Header', - defaultMessage: 'Our chef\'s ruined this recipe in the test kitchen, we suggest you try something else', - }, - message: { - id: '404.message', - description: '404 Message', - defaultMessage: 'Sorry the page came back with a 404 error we can\'t find what you are looking for', - } - }); - - return ( -
-

{formatMessage(messages.header)}

- 404 image -

{formatMessage(messages.message)}

-
- ); - } -})); diff --git a/frontend/modules/base/components/App.js b/frontend/modules/base/components/App.js deleted file mode 100644 index 7af7377d..00000000 --- a/frontend/modules/base/components/App.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' - -import Nav from '../../header/components/Nav' - -export default React.createClass({ - render: function() { - return ( -
-
- ); - } -}); diff --git a/frontend/modules/base/components/Footer.js b/frontend/modules/base/components/Footer.js index ffb2c516..bb1a1595 100644 --- a/frontend/modules/base/components/Footer.js +++ b/frontend/modules/base/components/Footer.js @@ -38,8 +38,4 @@ class Footer extends React.Component{ } } -Footer.propTypes = { - intl: intlShape.isRequired, -}; - export default injectIntl(Footer); diff --git a/frontend/modules/base/components/Loading.js b/frontend/modules/base/components/Loading.js new file mode 100755 index 00000000..54b8d555 --- /dev/null +++ b/frontend/modules/base/components/Loading.js @@ -0,0 +1,20 @@ +"use strict"; + +import React from 'react' +import PropTypes from 'prop-types' +import Spinner from 'react-spinkit'; + +const Loading = ({ message }) => { + return ( +
+

{ message }

+ +
+ ); +}; + +Loading.propTypes = { + message: PropTypes.string, +}; + +export default Loading diff --git a/frontend/modules/base/components/NotFound.js b/frontend/modules/base/components/NotFound.js new file mode 100644 index 00000000..087a99f5 --- /dev/null +++ b/frontend/modules/base/components/NotFound.js @@ -0,0 +1,34 @@ +import React from 'react' +import { + injectIntl, + IntlProvider, + defineMessages, + formatMessage +} from 'react-intl'; + +require("../css/404.scss"); + +const NotFound = ({ intl }) => { + const messages = defineMessages({ + header: { + id: '404.header', + description: '404 Header', + defaultMessage: 'Our chef\'s ruined this recipe in the test kitchen, we suggest you try something else', + }, + message: { + id: '404.message', + description: '404 Message', + defaultMessage: 'Sorry the page came back with a 404 error we can\'t find what you are looking for', + } + }); + + return ( +
+

{ intl.formatMessage(messages.header) }

+ 404 image +

{ intl.formatMessage(messages.message) }

+
+ ); +}; + +export default injectIntl(NotFound) \ No newline at end of file diff --git a/frontend/modules/base/css/core.css b/frontend/modules/base/css/core.css index c538ca94..9b8098b3 100644 --- a/frontend/modules/base/css/core.css +++ b/frontend/modules/base/css/core.css @@ -7,7 +7,7 @@ html { body { /* Margin bottom by footer height */ - margin-bottom: 60px; + margin-bottom: 80px; } .navbar { diff --git a/frontend/modules/browse/actions/BrowseActions.js b/frontend/modules/browse/actions/BrowseActions.js index 7ca44d39..268a86bb 100644 --- a/frontend/modules/browse/actions/BrowseActions.js +++ b/frontend/modules/browse/actions/BrowseActions.js @@ -1,6 +1,6 @@ import AppDispatcher from '../../common/AppDispatcher'; import Api from '../../common/Api'; -import { browserHistory } from 'react-router' +import history from '../../common/history' import DefaultFilters from '../constants/DefaultFilters' const BrowseActions = { @@ -29,6 +29,7 @@ const BrowseActions = { }, updateURL: function(filter) { + // TODO: use https://github.com/sindresorhus/query-string let encode_data = []; for (let key in filter) { if (filter[key]) { @@ -43,7 +44,7 @@ const BrowseActions = { path += '?' + encode_data.join('&'); } - browserHistory.push(path); + history.push(path); }, processLoadedRecipes: function(err, res) { diff --git a/frontend/modules/browse/components/Browse.js b/frontend/modules/browse/components/Browse.js index 463804ab..93cb0a96 100644 --- a/frontend/modules/browse/components/Browse.js +++ b/frontend/modules/browse/components/Browse.js @@ -1,6 +1,8 @@ import React from 'react' import classNames from 'classnames'; import SmoothCollapse from 'react-smooth-collapse'; +import queryString from 'query-string'; + import { injectIntl, IntlProvider, @@ -64,7 +66,7 @@ class Browse extends React.Component { CuisineStore.addChangeListener(this._onChangeCuisines); RatingStore.addChangeListener(this._onChangeRatings); - BrowseActions.browseInit(this.props.location.query); + BrowseActions.browseInit(queryString.parse(this.props.location.search)); } componentWillUnmount() { @@ -75,18 +77,20 @@ class Browse extends React.Component { } componentWillReceiveProps(nextProps) { - if (this.props.location.query.offset !== nextProps.location.query.offset) { - BrowseActions.loadRecipes(nextProps.location.query); - } else if (this.props.location.query.offset !== nextProps.location.query.offset) { - this.reloadData(nextProps.location.query); - } else if (this.props.location.query.course !== nextProps.location.query.course) { - this.reloadData(nextProps.location.query); - } else if (this.props.location.query.cuisine !== nextProps.location.query.cuisine) { - this.reloadData(nextProps.location.query); - } else if (this.props.location.query.rating !== nextProps.location.query.rating) { - this.reloadData(nextProps.location.query); - } else if (this.props.location.query.search !== nextProps.location.query.search) { - this.reloadData(nextProps.location.query); + let query = queryString.parse(this.props.location.search); + let nextQuery = queryString.parse(nextProps.location.search); + if (query.offset !== nextQuery.offset) { + BrowseActions.loadRecipes(nextQuery); + } else if (query.offset !== nextQuery.offset) { + this.reloadData(nextQuery); + } else if (query.course !== nextQuery.course) { + this.reloadData(nextQuery); + } else if (query.cuisine !== nextQuery.cuisine) { + this.reloadData(nextQuery); + } else if (query.rating !== nextQuery.rating) { + this.reloadData(nextQuery); + } else if (query.search !== nextQuery.search) { + this.reloadData(nextQuery); } } diff --git a/frontend/modules/browse/components/ListRecipes.js b/frontend/modules/browse/components/ListRecipes.js index 409c60b0..690b8acb 100644 --- a/frontend/modules/browse/components/ListRecipes.js +++ b/frontend/modules/browse/components/ListRecipes.js @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types'; -import { Link } from 'react-router' +import { Link } from 'react-router-dom' import Ratings from '../../recipe/components/Ratings'; diff --git a/frontend/modules/browse/stores/BrowseStore.js b/frontend/modules/browse/stores/BrowseStore.js index 18c0b563..54c23e6e 100644 --- a/frontend/modules/browse/stores/BrowseStore.js +++ b/frontend/modules/browse/stores/BrowseStore.js @@ -48,8 +48,4 @@ class BrowseStore extends EventEmitter { } }; -BrowseStore.propTypes = { - AppDispatcher: React.PropTypes.objectOf(AppDispatcher).isRequired -}; - module.exports = new BrowseStore(AppDispatcher); \ No newline at end of file diff --git a/frontend/modules/browse/stores/FilterStores.js b/frontend/modules/browse/stores/FilterStores.js index 52b38dc4..97310751 100644 --- a/frontend/modules/browse/stores/FilterStores.js +++ b/frontend/modules/browse/stores/FilterStores.js @@ -57,11 +57,6 @@ class FilterStore extends EventEmitter { } }; -FilterStore.propTypes = { - AppDispatcher: React.PropTypes.objectOf(AppDispatcher).isRequired, - name: React.PropTypes.string.isRequired -}; - module.exports.CuisineStore = new FilterStore(AppDispatcher, 'cuisine'); module.exports.CourseStore = new FilterStore(AppDispatcher, 'course'); module.exports.RatingStore = new FilterStore(AppDispatcher, 'rating'); \ No newline at end of file diff --git a/frontend/modules/common/authCheckRedirect.js b/frontend/modules/common/authCheckRedirect.js new file mode 100644 index 00000000..abb75bd7 --- /dev/null +++ b/frontend/modules/common/authCheckRedirect.js @@ -0,0 +1,10 @@ +import AuthStore from '../account/stores/AuthStore' +import history from './history' + +const authCheckRedirect = () => { + if (!AuthStore.isAuthenticated()) { + history.replace('/login'); + } +}; + +export default authCheckRedirect diff --git a/frontend/modules/common/bindIndexToActionCreators.js b/frontend/modules/common/bindIndexToActionCreators.js new file mode 100644 index 00000000..fc2cba19 --- /dev/null +++ b/frontend/modules/common/bindIndexToActionCreators.js @@ -0,0 +1,13 @@ + +const bindActionCreator = (actionCreator, index) => + (...args) => actionCreator(...args, index); + +const bindIndexToActionCreators = (actionCreators, index) => { + let transformed = {}; + Object.keys(actionCreators).forEach(key => { + transformed[key] = bindActionCreator(actionCreators[key], index) + }); + return transformed; +}; + +export default bindIndexToActionCreators \ No newline at end of file diff --git a/frontend/modules/common/form/FormComponents.js b/frontend/modules/common/form/FormComponents.js index eaffa140..0ec62ad6 100644 --- a/frontend/modules/common/form/FormComponents.js +++ b/frontend/modules/common/form/FormComponents.js @@ -150,17 +150,18 @@ class Checkbox extends BaseComponent { super(props); this.state = { - checked: this.props.checked || true + checked: !!this.props.checked }; } handleChange(event) { + let checked = !this.state.checked; this.setState({ - checked: this.state.checked === true ? 1 : 0 + checked: checked }); if(this.props.change) { - this.props.change(event.target.name, new_value); + this.props.change(event.target.name, checked); } } @@ -176,13 +177,12 @@ class Checkbox extends BaseComponent { return (
- + + { this.props.placeholder }
) diff --git a/frontend/modules/common/history.js b/frontend/modules/common/history.js new file mode 100644 index 00000000..3247f6c8 --- /dev/null +++ b/frontend/modules/common/history.js @@ -0,0 +1,5 @@ +import { createBrowserHistory } from 'history' + +export default createBrowserHistory({ + /* pass a configuration object here if needed */ +}) \ No newline at end of file diff --git a/frontend/modules/common/reducer.js b/frontend/modules/common/reducer.js new file mode 100644 index 00000000..2d0fd8e9 --- /dev/null +++ b/frontend/modules/common/reducer.js @@ -0,0 +1,10 @@ +import { combineReducers } from 'redux' +import { default as list } from '../list/reducers/GroceryListReducer' +import { default as recipe } from '../recipe/reducers/Reducer' + +const reducer = combineReducers({ + list, + recipe, +}); + +export default reducer diff --git a/frontend/modules/header/components/GroceryListMenuItem.js b/frontend/modules/header/components/GroceryListMenuItem.js index d4cb97f3..53f7db41 100644 --- a/frontend/modules/header/components/GroceryListMenuItem.js +++ b/frontend/modules/header/components/GroceryListMenuItem.js @@ -33,7 +33,7 @@ class GroceryListMenuItem extends React.Component { id="basic-nav-dropdown"> { lists } {( this.props.data.length > 0 ? : null )} - + { formatMessage(messages.grocery_list) } diff --git a/frontend/modules/header/components/Nav.js b/frontend/modules/header/components/Nav.js index 620a9679..ecab1b84 100644 --- a/frontend/modules/header/components/Nav.js +++ b/frontend/modules/header/components/Nav.js @@ -1,5 +1,5 @@ import React from 'react' -import { Link } from 'react-router' +import { Link } from 'react-router-dom' import { injectIntl, IntlProvider, @@ -10,8 +10,6 @@ import { Image, Navbar, Nav, NavDropdown, MenuItem, NavItem } from 'react-bootst import { LinkContainer } from 'react-router-bootstrap' import AuthStore from '../../account/stores/AuthStore'; -import { ListStore, CHANGE_EVENT } from '../../list/stores/ListStore'; -import ListActions from '../../list/actions/ListActions'; import { CreateRecipeMenuItem } from './CreateRecipeMenuItem' import { GroceryListMenuItem } from './GroceryListMenuItem' @@ -29,15 +27,13 @@ class NavBar extends React.Component { componentDidMount() { AuthStore.addChangeListener(this._onChange); - ListStore.addChangeListener(CHANGE_EVENT, this._onChange); if (AuthStore.isAuthenticated()) { - ListActions.init(); + this.props.listActions.load(); } } componentWillUnmount() { AuthStore.removeChangeListener(this._onChange); - ListStore.removeChangeListener(CHANGE_EVENT, this._onChange); } _getState() { @@ -46,13 +42,12 @@ class NavBar extends React.Component { // If it is we need to init the list store so the menu has teh users lists. if (this.hasOwnProperty('state')) { if (!this.state.authenticated && authenticated) { - ListActions.init() + this.props.listActions.load() } } return { authenticated: authenticated, - lists: ListStore.get_lists() || [] }; } @@ -102,7 +97,7 @@ class NavBar extends React.Component { : null )} {( this.state.authenticated ? - : null + : null )}