index.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import React, { Component } from 'react';
  2. import { RouteComponentProps } from 'react-router';
  3. import Navbar from 'components/Navbar';
  4. import { AppState } from 'store/configureStore';
  5. import { connect } from 'react-redux';
  6. import { PlannerState, Weekday, Meal, DayPlan, PlannerMode, RecipePlan, WeekPlan } from 'types/planner';
  7. import Card from 'components/Card';
  8. import PlannerActions, { fetchPlannerActionCreator, PlannerActions as PlannerActionsTypes, savePlannerActionCreator } from './actions';
  9. import moment from 'moment';
  10. import { DragDropContext, Droppable, Draggable, DropResult, OnDragEndResponder } from 'react-beautiful-dnd';
  11. import './styles.scss';
  12. import { getWeekDay } from 'services/time';
  13. import Button from 'components/Button';
  14. import { ThunkDispatch } from 'redux-thunk';
  15. import { bindActionCreators } from 'redux';
  16. type Actions = typeof PlannerActions
  17. interface PlannerContainerProps extends RouteComponentProps, PlannerState, Actions {
  18. fetch: typeof fetchPlannerActionCreator,
  19. save: typeof savePlannerActionCreator
  20. }
  21. interface PlannerContainerState {
  22. week: [Weekday, string][]
  23. }
  24. class PlannerContainer extends Component<PlannerContainerProps, PlannerContainerState> {
  25. constructor(props: PlannerContainerProps) {
  26. super(props);
  27. this.state = {
  28. week: Object.keys(props.planner).map((weekday) => ([ weekday as Weekday, moment(props.planner[weekday as Weekday].date).format()]))
  29. }
  30. }
  31. componentDidMount() {
  32. this.props.fetch(this.props.week);
  33. }
  34. componentDidUpdate() {
  35. const { isFetching } = this.props;
  36. }
  37. assignRecipe = (result: DropResult) => {
  38. const recipe = this.findRecipe(result.draggableId);
  39. if (result.destination && recipe) {
  40. const [idx, day, meal] = result.destination.droppableId.split('-');
  41. this.props.assignToDay(recipe, day as Weekday, meal as Meal);
  42. this.props.removeFromBacklog(recipe);
  43. this.props.editPlanner();
  44. }
  45. }
  46. removeMeal = (day: Weekday, meal: Meal) => {
  47. this.props.editPlanner();
  48. return this.props.removeMeal(day, meal);
  49. }
  50. findRecipe = (recipeId: string) => this.props.backlog.find(recipe => recipe._id === recipeId);
  51. changeMode = (mode: PlannerMode) => this.props.changePlannerMode(mode);
  52. render () {
  53. return (
  54. <div className='cbk-planner'>
  55. <Navbar
  56. title='Planner'
  57. >
  58. {
  59. this.props.mode === 'view' ? (
  60. <Button outlined raised onClick={() => this.changeMode('edit')}>Edit</Button>
  61. ) : (
  62. <Button outlined raised onClick={() => {
  63. if (this.props.edit) {
  64. this.props.save(this.props.planner, this.props.from, this.props.to)
  65. }
  66. this.changeMode('view')
  67. }}>Save</Button>
  68. )
  69. }
  70. </Navbar>
  71. <DisplayPlanner
  72. mode={this.props.mode}
  73. week={this.state.week}
  74. weekNumber={this.props.week}
  75. onDragEnd={this.assignRecipe}
  76. backlog={this.props.backlog}
  77. planner={this.props.planner}
  78. removeMeal={this.removeMeal}
  79. />
  80. </div>
  81. );
  82. }
  83. }
  84. const DisplayMealWithActions: React.SFC<{
  85. recipe?: RecipePlan,
  86. children?: React.ReactElement
  87. }> = ({ recipe, children }) => recipe ? (
  88. <div className='meal-card'>
  89. <div className="meal-card--actions">
  90. { children }
  91. </div>
  92. <h5>{ recipe.name }</h5>
  93. </div>
  94. ) : null;
  95. const DisplayPlanner: React.SFC<{
  96. week: [Weekday, string][],
  97. onDragEnd: OnDragEndResponder,
  98. weekNumber: number,
  99. backlog: RecipePlan[],
  100. planner: WeekPlan,
  101. removeMeal: typeof PlannerActions.removeMeal,
  102. mode?: PlannerMode,
  103. }> = ({ mode, onDragEnd, backlog, weekNumber, week, planner, removeMeal}) => (
  104. <section className='cbk-planner__body'>
  105. <DragDropContext onDragEnd={onDragEnd}>
  106. {
  107. mode === 'edit' ? (
  108. <div className='cbk-planner__body__backlog'>
  109. <Droppable droppableId='recipeList'>
  110. {(provided) => (
  111. <div ref={provided.innerRef}>
  112. {backlog.map((item, index) => (
  113. <Draggable
  114. key={item._id}
  115. draggableId={item._id}
  116. index={index}>
  117. {provided => (
  118. <div
  119. ref={provided.innerRef}
  120. {...provided.draggableProps}
  121. {...provided.dragHandleProps}
  122. >
  123. <Card
  124. key={item._id}
  125. title={item.name}
  126. onClick={() => {}}
  127. />
  128. </div>
  129. )}
  130. </Draggable>
  131. ))}
  132. {provided.placeholder}
  133. </div>
  134. )}
  135. </Droppable>
  136. </div>
  137. ) : null
  138. }
  139. <div className='cbk-planner__body__calendar'>
  140. <div>Week {weekNumber}</div>
  141. <div className='container'>
  142. <div className='day-schedule day-schedule--meals'>
  143. <div className='day-schedule--date'></div>
  144. <div className='day-schedule--lunch'> <h5>lunch</h5></div>
  145. <div className='day-schedule--dinner'> <h5>dinner</h5> </div>
  146. </div>
  147. {
  148. week.map(([weekday, day], dayNumber) => (
  149. <div key={dayNumber} className='day-schedule'>
  150. <div className='day-schedule--date'>
  151. <h5>{weekday} {moment(day).format('DD')}</h5>
  152. </div>
  153. <div className='day-schedule--lunch'>
  154. <Droppable droppableId={`${dayNumber}-${weekday}-lunch`}>
  155. {provided => (
  156. <div className='day-schedule-content' ref={provided.innerRef}>
  157. {provided.placeholder}
  158. <DisplayMealWithActions recipe={planner[weekday]['lunch']}>
  159. {
  160. planner[weekday]['lunch'] && mode === 'edit' ? (
  161. <Button icon='clear' onClick={() => removeMeal(weekday, 'lunch')} small></Button>
  162. ) : undefined
  163. }
  164. </DisplayMealWithActions>
  165. </div>
  166. )}
  167. </Droppable>
  168. </div>
  169. <div className='day-schedule--dinner'>
  170. <Droppable droppableId={`${dayNumber}-${weekday}-dinner`}>
  171. {provided => (
  172. <div className='day-schedule-content' ref={provided.innerRef}>
  173. {provided.placeholder}
  174. <DisplayMealWithActions recipe={planner[weekday]['dinner']}>
  175. {
  176. planner[weekday]['dinner'] && mode === 'edit' ? (
  177. <Button icon='clear' onClick={() => removeMeal(weekday, 'dinner')} small></Button>
  178. ) : undefined
  179. }
  180. </DisplayMealWithActions>
  181. </div>
  182. )}
  183. </Droppable>
  184. </div>
  185. </div>
  186. ))
  187. }
  188. </div>
  189. </div>
  190. </DragDropContext>
  191. </section>
  192. )
  193. const mapStateToProps = (state: AppState) => {
  194. return state.planner
  195. }
  196. const mapDispatchToProps = (dispatch: ThunkDispatch<AppState, any, any>) => ({
  197. fetch: (week: number) => dispatch(fetchPlannerActionCreator(week)),
  198. save: (plan: WeekPlan, from: Date, to: Date) => dispatch(savePlannerActionCreator(plan, from, to)),
  199. ...bindActionCreators(PlannerActions, dispatch)
  200. })
  201. export default connect(
  202. mapStateToProps,
  203. mapDispatchToProps
  204. )(PlannerContainer);