Sfoglia il codice sorgente

AddAll to shopping cart button. Clean up shopping cart reducer. Create ShoppingRecipe type that includes ingredients instead of all Recipe fields

Tatiana Inama 7 anni fa
parent
commit
ce49451748

+ 20 - 145
recipes/src/containers/Planner/index.tsx

@@ -4,23 +4,23 @@ import Navbar from 'components/Navbar';
 import { AppState } from 'store/configureStore';
 import { connect } from 'react-redux';
 import { PlannerState, Weekday, Meal, PlannerMode, RecipePlan, WeekPlan, Meals, WeekShift } from 'types/planner';
-import Card from 'components/Card';
 import PlannerActions, { fetchPlannerActionCreator, savePlannerActionCreator, changePlannerRangeActionCreator } from './actions';
+import ShoppingCartActions from 'containers/ShoppingCart/actions';
 import moment, { Moment } from 'moment';
-import { DragDropContext, Droppable, Draggable, DropResult, OnDragEndResponder } from 'react-beautiful-dnd';
 import './styles.scss';
 import Button from 'components/Button';
 import { ThunkDispatch } from 'redux-thunk';
 import { bindActionCreators } from 'redux';
 import RecipeSearch from 'components/RecipeSearch';
-import { DBRecipe } from 'types/recipes';
 import { Display, UiState} from 'types/ui';
 import { getWeekPeriod } from 'services/time';
 import Sticker from 'components/Sticker';
+import { DBRecipe } from 'src/types/recipes';
 
 type Actions = typeof PlannerActions
+type ShoppingCartActionsType = typeof ShoppingCartActions;
 
-interface PlannerContainerProps extends RouteComponentProps, PlannerState, Actions, UiState {
+interface PlannerContainerProps extends RouteComponentProps, PlannerState, Actions, ShoppingCartActionsType, UiState {
   fetch: typeof fetchPlannerActionCreator,
   save: typeof savePlannerActionCreator
   changeRange: typeof changePlannerRangeActionCreator
@@ -34,6 +34,7 @@ class PlannerContainer extends Component<PlannerContainerProps, PlannerContainer
 
   constructor(props: PlannerContainerProps) {
     super(props);
+    console.log(props)
     this.state = {
       week: Object.keys(props.planner).map((weekday) => ([ weekday as Weekday, moment(props.planner[weekday as Weekday].date).format()])),
     }
@@ -57,16 +58,6 @@ class PlannerContainer extends Component<PlannerContainerProps, PlannerContainer
     }
   }
 
-  assignRecipe = (result: DropResult) => {
-    const recipe = this.findRecipe(result.draggableId);
-    if (result.destination && recipe) {
-      const [idx, day, meal] = result.destination.droppableId.split('-');
-      this.props.assignToDay(recipe, day as Weekday, parseInt(meal) as Meal);
-      this.props.removeFromBacklog(recipe);
-      this.props.editPlanner();
-    }
-  }
-
   removeMeal = (day: Weekday, meal: Meal) => {
     this.props.editPlanner();
     return this.props.removeMeal(day, meal);
@@ -87,6 +78,16 @@ class PlannerContainer extends Component<PlannerContainerProps, PlannerContainer
     this.props.history.push('/recipes/view/' + recipeId)
   }
 
+  getRecipes = (planner: WeekPlan): RecipePlan[] => {
+    const nonEmpty = (recipe: RecipePlan | undefined): recipe is RecipePlan => recipe !== undefined;
+    return Object.entries(planner).reduce((recipes, [weekday, dayplan]) => {
+      return [
+        ...recipes,
+        ...Meals.map(meal => dayplan[meal]).filter(nonEmpty),
+      ];
+    }, [] as RecipePlan[]);
+  }
+
   render () {
     return (
       <div className='cbk-planner'>
@@ -134,18 +135,9 @@ class PlannerContainer extends Component<PlannerContainerProps, PlannerContainer
                 removeMeal={this.removeMeal}
                 goTo={this.goToRecipe}
               />
-              {/* <DisplayPlannerDrag
-                mode={this.props.mode}
-                week={this.state.week}
-                weekNumber={this.props.week}
-                onDragEnd={this.assignRecipe}
-                backlog={this.props.backlog}
-                planner={this.props.planner}
-                removeMeal={this.removeMeal}
-                addToBacklog={this.props.addToBacklog}
-                assignToDay={this.props.assignToDay}
-                changeWeek={this.changeWeek}
-              /> */}
+              <div className='cbk-planner__shopping'>
+                <Button outlined onClick={() => { this.props.addAll(this.getRecipes(this.props.planner))}}>Add to shopping</Button>
+              </div>
             </>
           ) 
           :(<MobileDisplayPlanner 
@@ -274,124 +266,6 @@ const MobileDisplayPlanner: React.SFC<{
   </section>
 )
 
-const DisplayPlannerDrag: React.SFC<{
-  week: [Weekday, string][],
-  onDragEnd: OnDragEndResponder,
-  weekNumber: number,
-  backlog: RecipePlan[],
-  planner: WeekPlan,
-  removeMeal: typeof PlannerActions.removeMeal,
-  assignToDay: typeof PlannerActions.assignToDay,
-  addToBacklog: (recipe: DBRecipe) => {},
-  changeWeek: (shift?: WeekShift) => void,
-  mode?: PlannerMode,
-}> = ({ mode, onDragEnd, backlog, weekNumber, week, planner, removeMeal, addToBacklog, assignToDay, changeWeek}) => (
-  <section className='cbk-planner__body'>
-    <DragDropContext onDragEnd={onDragEnd}>
-      {
-        mode === PlannerMode.Edit ? (
-          <div className='cbk-planner-dnd__body__backlog'>
-            <RecipeSearch onSelect={(selected)=>{ addToBacklog(selected) }}/>
-            <Droppable droppableId='recipeList'>
-              {(provided) => (
-                <div ref={provided.innerRef}>
-                  {backlog.map((item, index) => (
-                      <Draggable
-                          key={item._id}
-                          draggableId={item._id}
-                          index={index}>
-                          {provided => (
-                            <div
-                              ref={provided.innerRef}
-                              {...provided.draggableProps}
-                              {...provided.dragHandleProps}
-                            >
-                              <Card
-                                key={item._id}
-                                title={item.name}
-                                onClick={() => {}}
-                              />
-                            </div>
-                          )}
-                      </Draggable>
-                  ))}
-                  {provided.placeholder}
-              </div>
-              )}
-            </Droppable>
-          </div>
-        ) : null
-      }
-      <div className='cbk-planner-dnd__body__calendar'>
-        <div>
-          Week {weekNumber}
-          </div>
-          <Button onClick={() => changeWeek(WeekShift.Prev)}>Prev</Button>
-          <Button onClick={() => changeWeek()}>Current</Button>
-          <Button onClick={() => changeWeek(WeekShift.Next)}>Next</Button>
-        <div className='container'>
-          <div className='day-schedule day-schedule--meals'>
-            <div className='day-schedule--date'></div>
-            {
-              Meals.map((meal, key) => (
-                <div className='day-schedule--meal' key={key}>
-                  <h5>{Meal[meal]}</h5>
-                </div>
-              ))
-            }
-          </div>
-          {
-            week.map(([weekday, day], dayNumber) => (
-              <div key={dayNumber} className='day-schedule'>
-                <div className='day-schedule--date'>
-                  <h5>{planner[weekday].date.format('ddd DD.MM') || weekday}</h5>
-                </div>
-                {
-                  Meals.map((meal, key) => {
-                    const recipe = planner[weekday][meal];
-                    return (
-                      <div className='day-schedule--meal' key={key}>
-                        {
-                          mode === PlannerMode.Edit && !recipe ? 
-                            (<RecipeSearch onSelect={(selected)=>{ assignToDay(selected, weekday, meal) }}/>) :
-                            null
-                        }
-                        <Droppable droppableId={`${dayNumber}-${weekday}-${meal}`}>
-                          {
-                            provided => (
-                              <div className='day-schedule-content' ref={provided.innerRef}>
-                                { provided.placeholder }
-                                {
-                                  recipe ? (
-                                    <div className='meal-card'>
-                                      <div className="meal-card--actions">
-                                        {
-                                          recipe && mode === PlannerMode.Edit ? (
-                                            <Button icon='clear' onClick={() => removeMeal(weekday, meal)} small></Button>      
-                                          ) : null
-                                        }
-                                      </div>
-                                      <h5>{ recipe.name }</h5>
-                                    </div>
-                                  ) : null
-                                }
-                              </div>
-                            )
-                          }
-                        </Droppable>
-                      </div>
-                    )
-                  })
-                }
-              </div>
-            ))
-          }
-        </div>
-      </div>
-    </DragDropContext>
-  </section>
-)
-
 const mapStateToProps = (state: AppState) => {
   return {
     ...state.planner,
@@ -403,7 +277,8 @@ const mapDispatchToProps = (dispatch: ThunkDispatch<AppState, any, any>) => ({
   fetch: (from: Moment, to: Moment) => dispatch(fetchPlannerActionCreator(from, to)),
   save: (plan: WeekPlan, from: Moment, to: Moment) => dispatch(savePlannerActionCreator(plan, from, to)),
   changeRange: (from: Moment, to: Moment, shift: WeekShift) => dispatch(changePlannerRangeActionCreator(from, to, shift)),
-  ...bindActionCreators(PlannerActions, dispatch)
+  ...bindActionCreators(PlannerActions, dispatch),
+  ...bindActionCreators(ShoppingCartActions, dispatch)
 })
 
 export default connect(

+ 25 - 6
recipes/src/containers/ShoppingCart/actions.ts

@@ -1,18 +1,19 @@
-import { DBRecipe } from 'types/recipes';
-import ShoppingItem from 'types/shopping-cart';
+import { DBRecipe, SubRecipe } from 'types/recipes';
+import { ShoppingRecipe } from 'types/shopping-cart';
 import { bindActionCreators } from 'redux';
 
 export const ADD_RECIPE_TO_CART = 'ADD_ RECIPE_TO_CART';
 export const REMOVE_RECIPE_FROM_CART = 'REMOVE_RECIPE_FROM_CART';
 export const REMOVE_ITEM_FROM_CART = 'REMOVE_ITEM_FROM_CART';
 export const REMOVE_ALL = 'REMOVE_ALL';
+export const ADD_ALL = 'ADD_ALL';
 
-export const addRecipeToCart = (recipe: DBRecipe): AddRecipeToCart => ({
+export const addRecipeToCart = (recipe: ShoppingRecipe): AddRecipeToCart => ({
   type: ADD_RECIPE_TO_CART,
   payload: recipe
 })
 
-export const removeFromCart = (recipe: DBRecipe): RemoveRecipeFromCart => ({
+export const removeFromCart = (recipe: ShoppingRecipe): RemoveRecipeFromCart => ({
   type: REMOVE_RECIPE_FROM_CART,
   payload: recipe._id
 })
@@ -26,9 +27,14 @@ export const removeAll = (): RemoveAll => ({
   type: REMOVE_ALL
 });
 
+export const addAll = (recipes: ShoppingRecipe[]) => ({
+  type: ADD_ALL,
+  payload: recipes
+})
+
 export type AddRecipeToCart = {
   type: typeof ADD_RECIPE_TO_CART,
-  payload: DBRecipe
+  payload: ShoppingRecipe
 }
 
 export type RemoveRecipeFromCart = {
@@ -45,4 +51,17 @@ export type RemoveAll = {
   type: typeof REMOVE_ALL
 }
 
-export type ActionTypes = AddRecipeToCart | RemoveRecipeFromCart | RemoveItemFromCart | RemoveAll;
+export type AddAll = {
+  type: typeof ADD_ALL,
+  payload: ShoppingRecipe[]
+}
+
+export type ActionTypes = AddRecipeToCart | RemoveRecipeFromCart | RemoveItemFromCart | RemoveAll | AddAll;
+
+export default {
+  addRecipeToCart,
+  removeFromCart,
+  removeItemFromCart,
+  removeAll,
+  addAll
+}

+ 15 - 146
recipes/src/containers/ShoppingCart/reducers.ts

@@ -1,8 +1,6 @@
 import ShoppingItem from 'types/shopping-cart';
-import { ActionTypes, ADD_RECIPE_TO_CART, REMOVE_RECIPE_FROM_CART, REMOVE_ITEM_FROM_CART, REMOVE_ALL } from './actions';
-import { DBRecipe, Ingredient } from 'src/types/recipes';
-import { update } from 'ramda';
-import { Convert, GetMeasure, Measure } from 'services/measurements';
+import { ActionTypes, ADD_RECIPE_TO_CART, REMOVE_RECIPE_FROM_CART, REMOVE_ITEM_FROM_CART, REMOVE_ALL, ADD_ALL } from './actions';
+import { createShoppingList, getItemsFromRecipe } from './services';
 
 export type ShoppingCartState = {
   items: ShoppingItem[],
@@ -10,150 +8,10 @@ export type ShoppingCartState = {
 }
 
 const initialState: ShoppingCartState = {
-    items: [
-      {
-        name: 'chicken breast',
-        quantity: 113.4,
-        unit: 'g',
-        _original: '4 6oz Chicken Breast',
-        suggestions: [],
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken', 'Fried Chicken']
-      },
-      {
-        name: 'eggs',
-        quantity: 2,
-        unit: '',
-        _original: '2 Eggs',
-        suggestions: [],
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken']
-      },
-      {
-        name: 'freshly grated parm',
-        quantity: 0,
-        unit: '',
-        _original: 'Freshly Grated Parm',
-        suggestions: [],
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken']
-      },
-      {
-        name: 'bread crumbs',
-        quantity: 1.5,
-        unit: 'cup',
-        _original: '1-1/2 cups of Bread Crumbs',
-        suggestions: [
-          {
-            _id: '5d358c0be26a103030879377',
-            name: 'bread flour',
-            variants: [
-              'bread flour'
-            ],
-            equivalence: 127,
-            referenceUnit: 'cup',
-            prefferedUnit: 'g',
-            translation: {
-              dutch: 'tarwebloem'
-            },
-            measure: 2
-          }
-        ],
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken']
-      },
-      {
-        name: 'salt and pepper, to taste',
-        quantity: 0,
-        unit: '',
-        _original: 'Salt and Pepper, to taste',
-        suggestions: [],
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken']
-      },
-      {
-        name: 'light olive oil for shallow frying',
-        quantity: 0,
-        unit: '',
-        _original: 'Light Olive Oil for shallow Frying',
-        recipeId: '5d37045aef8ef87af296cba7',
-        recipeName: ['Chicken']
-      },
-      {
-        name: 'olive oil',
-        quantity: 3,
-        unit: 'tbsp',
-        _original: '3 Tbsp of Olive Oil',
-        recipeId: '5d37045aef8ef87af296cba7',
-      },
-    ],
-    recipesId: [
-      '5d37045aef8ef87af296cba7'
-    ]
+    items: [],
+    recipesId: []
 };
 
-const getItemsFromRecipe = (recipe: DBRecipe): ShoppingItem[] => {
-  return recipe.ingredients.reduce((ingredients, subRecipe) => {
-    return [
-      ...ingredients,
-      ...subRecipe.ingredients.map(createShoppingItem(recipe._id, recipe.name)),
-    ]
-  }, [] as ShoppingItem[])
-} 
-
-const combineItems = (a: ShoppingItem, b: ShoppingItem): ShoppingItem => {
-  if (a.unit && b.unit) {
-    const m = GetMeasure(b.unit);
-    return {
-      ...a,
-      quantity: a.quantity + Convert(b.quantity, b.unit, a.unit, m.name as Measure)
-    }
-  } else {
-    if (a.unit !== b.unit) {
-      throw Error(`cannot sum this units: ${b.unit, a.unit}`)
-    } else {
-      return {
-        ...a,
-        quantity: a.quantity + b.quantity
-      }
-    }
-  }
-}
-
-const createShoppingList = (initial: ShoppingItem[], newItems: ShoppingItem[]): ShoppingItem[] => {
-  return newItems.reduce((shoppingCart, newItem) => {
-    const existentItemIdx = shoppingCart.findIndex(item => item.name === newItem.name);
-    if ( existentItemIdx > -1 ) {
-      try {
-        const sum = combineItems(initial[existentItemIdx], newItem).quantity
-        return update(existentItemIdx, {
-          ...initial[existentItemIdx],
-          recipeName: (initial[existentItemIdx].recipeName||[]).concat(newItem.recipeName||[]),
-          quantity: sum
-        }, shoppingCart)
-      } catch {
-        return [
-          ...shoppingCart,
-          newItem
-        ]
-      }
-    } else {
-      return [
-        ...shoppingCart,
-        newItem
-      ]
-    }
-  }, initial);
-}
-
-const createShoppingItem = (recipeId: string, recipeName: string) => (ingredient: Ingredient): ShoppingItem => {
-  return {
-    ...ingredient,
-    recipeId,
-    recipeName: [recipeName]
-  };
-}
-
 const shoppingCartReducer = (
   state = initialState,
   action: ActionTypes
@@ -183,6 +41,17 @@ const shoppingCartReducer = (
         items: [],
         recipesId: []
       }
+    case ADD_ALL:
+      const all = action.payload.reduce((items, x) => {
+        return [
+          ...items,
+          ...getItemsFromRecipe(x)
+        ];
+      }, [] as ShoppingItem[])
+      return {
+        ...state,
+        items: createShoppingList(state.items, all)
+      }
     default:
       return state
   }

+ 73 - 0
recipes/src/containers/ShoppingCart/services.ts

@@ -0,0 +1,73 @@
+import ShoppingItem, { ShoppingRecipe } from 'types/shopping-cart';
+import { Ingredient } from 'types/recipes';
+import { Convert, GetMeasure, Measure } from 'services/measurements';
+import { update } from 'ramda';
+
+const getItemsFromRecipe = (recipe: ShoppingRecipe): ShoppingItem[] => {
+  return recipe.ingredients.reduce((ingredients, subRecipe) => {
+    return [
+      ...ingredients,
+      ...subRecipe.ingredients.map(createShoppingItem(recipe._id, recipe.name)),
+    ]
+  }, [] as ShoppingItem[])
+} 
+
+const combineItems = (a: ShoppingItem, b: ShoppingItem): ShoppingItem => {
+  if (a.unit && b.unit) {
+    const m = GetMeasure(b.unit);
+    return {
+      ...a,
+      quantity: a.quantity + Convert(b.quantity, b.unit, a.unit, m.name as Measure)
+    }
+  } else {
+    if (a.unit !== b.unit) {
+      throw Error(`cannot sum this units: ${b.unit, a.unit}`)
+    } else {
+      return {
+        ...a,
+        quantity: a.quantity + b.quantity
+      }
+    }
+  }
+}
+
+const createShoppingList = (initial: ShoppingItem[], newItems: ShoppingItem[]): ShoppingItem[] => {
+  return newItems.reduce((shoppingCart, newItem) => {
+    const existentItemIdx = shoppingCart.findIndex(item => item.name === newItem.name);
+    if ( existentItemIdx > -1 ) {
+      try {
+        const sum = combineItems(initial[existentItemIdx], newItem).quantity
+        return update(existentItemIdx, {
+          ...initial[existentItemIdx],
+          recipeName: (initial[existentItemIdx].recipeName||[]).concat(newItem.recipeName||[]),
+          quantity: sum
+        }, shoppingCart)
+      } catch {
+        return [
+          ...shoppingCart,
+          newItem
+        ]
+      }
+    } else {
+      return [
+        ...shoppingCart,
+        newItem
+      ]
+    }
+  }, initial);
+}
+
+const createShoppingItem = (recipeId: string, recipeName: string) => (ingredient: Ingredient): ShoppingItem => {
+  return {
+    ...ingredient,
+    recipeId,
+    recipeName: [recipeName]
+  };
+}
+
+export {
+  getItemsFromRecipe,
+  combineItems,
+  createShoppingList,
+  createShoppingItem
+}

+ 2 - 0
recipes/src/types/planner.ts

@@ -1,3 +1,4 @@
+import { SubRecipe } from 'types/recipes';
 import { Moment } from "moment";
 
 export type Weekday =
@@ -18,6 +19,7 @@ export enum Meal {
 export type RecipePlan = {
   _id: string,
   name: string,
+  ingredients: SubRecipe[]
 }
 
 export type DayPlan = {

+ 7 - 1
recipes/src/types/shopping-cart.ts

@@ -1,8 +1,14 @@
-import Recipe, { Ingredient } from './recipes';
+import Recipe, { Ingredient, SubRecipe } from './recipes';
 
 interface ShoppingItem extends Ingredient {
   recipeId?: string,
   recipeName?: string[]
 }
 
+export interface ShoppingRecipe {
+  _id: string,
+  name: string,
+  ingredients: SubRecipe[]
+}
+
 export default ShoppingItem;