Last active
February 14, 2021 09:52
-
-
Save caprosset/2942697f4fdb42e2f6d34135ef6b7ad1 to your computer and use it in GitHub Desktop.
LAB Solution | React IronNutrition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/AddNewFood.js | |
import React, { Component } from 'react'; | |
class AddNewFood extends Component { | |
state = { | |
name: "", | |
calories: "", | |
image: "" | |
} | |
handleChange = e => { | |
let { name, value } = e.target; | |
this.setState({[name]: value}) | |
} | |
handleSubmit = e => { | |
e.preventDefault(); | |
// creamos un objeto newFood que contiene el state | |
const newFood = this.state; | |
// invocamos la función creada en FoodList.js pasándole el objeto comida que se acaba de crear | |
this.props.addFood(newFood); | |
// vacíamos el formulario | |
this.setState({ | |
name: "", | |
calories: "", | |
image: "" | |
}) | |
} | |
render() { | |
return ( | |
<div> | |
<form onSubmit={this.handleSubmit}> | |
<div className="field"> | |
<label className="label">Name</label> | |
<div className="control"> | |
<input | |
className="input" | |
type="text" | |
name="name" | |
value={this.state.name} | |
onChange={this.handleChange} /> | |
</div> | |
</div> | |
<div className="field"> | |
<label className="label">Calories</label> | |
<div className="control"> | |
<input | |
className="input" | |
type="number" | |
name="calories" | |
value={this.state.calories} | |
onChange={this.handleChange} /> | |
</div> | |
</div> | |
<div className="field"> | |
<label className="label">Image</label> | |
<div className="control"> | |
<input | |
className="input" | |
type="text" | |
name="image" | |
value={this.state.image} | |
onChange={this.handleChange} /> | |
</div> | |
</div> | |
<div className="control"> | |
<button type="submit" className="button is-link"> | |
Submit new food | |
</button> | |
</div> | |
</form> | |
</div> | |
) | |
} | |
} | |
export default AddNewFood; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/App.css | |
.container { | |
display: flex; | |
} | |
.container .section { | |
width: 50%; | |
} | |
#add-section { | |
margin: 30px 0; | |
} | |
#todays-food-title { | |
font-size: 24px; | |
font-weight: bold; | |
} | |
li { | |
display: flex; | |
align-items: center; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/App.js | |
import React from 'react'; | |
import './App.css'; | |
import FoodList from './components/FoodList'; | |
const App = () => { | |
return ( | |
<div className="App"> | |
<FoodList /> | |
</div> | |
) | |
} | |
export default App; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/FoodBox.js | |
import React, {Component} from 'react'; | |
import './../App.css'; | |
class FoodBox extends Component { | |
state = { | |
quantity: 1 | |
} | |
handleChange = e => { | |
const { value } = e.target; | |
this.setState({ quantity: Number(value) }) | |
} | |
render() { | |
const { name, calories, image } = this.props.food; | |
return ( | |
<div className="box"> | |
<article className="media"> | |
<div className="media-left"> | |
<figure className="image is-64x64"> | |
<img src={image} width="200" height="150" /> | |
</figure> | |
</div> | |
<div className="media-content"> | |
<div className="content"> | |
<p> | |
<strong>{name}</strong> <br /> | |
<small>{calories} cal</small> | |
</p> | |
</div> | |
</div> | |
<div className="media-right"> | |
<div className="field has-addons"> | |
<div className="control"> | |
<input | |
className="input" | |
type="number" | |
value={this.state.quantity} | |
onChange={this.handleChange} | |
/> | |
</div> | |
<div className="control"> | |
<button | |
onClick={() => this.props.updateTodaysFood({...this.props.food, quantity: this.state.quantity})} | |
className="button is-info"> | |
+ | |
</button> | |
</div> | |
</div> | |
</div> | |
</article> | |
</div> | |
) | |
} | |
} | |
export default FoodBox; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/FoodList.js | |
import React, { Component } from 'react'; | |
import shortid from 'shortid'; | |
import foods from './../foods.json'; | |
import FoodBox from './FoodBox'; | |
import AddNewFood from './AddNewFood'; | |
import Search from './Search'; | |
import TodaysFood from './TodaysFood'; | |
class FoodList extends Component { | |
state = { | |
foodsList: foods, | |
filteredFoodsList: foods, | |
showForm: false, | |
todaysFood: [], | |
totalCalories: 0 | |
} | |
// ITERACIÓN 3 - ADD NEW FOOD | |
toggleForm = () => { | |
// a cada clic del usuario, el estado de showForm cambiará por su contrario (si el formulario está escondido se mostrará, y vice-versa) | |
this.setState({showForm: !this.state.showForm}); | |
} | |
// ITERACIÓN 3 - ADD NEW FOOD | |
addFood = (newFoodObj) => { | |
// hacer una copia de la lista original de comidas | |
const foodsListCopy = [...this.state.foodsList]; | |
// añadir la nueva comida en 1era posición del array | |
foodsListCopy.unshift(newFoodObj); | |
// resetear el state | |
this.setState({filteredFoodsList: foodsListCopy}); | |
// una vez la comida está añadida a la lista, esconder el formulario | |
this.state.showForm = false; | |
} | |
// ITERACIÓN 4 - SEARCH BAR | |
filterFoods = (searchTerm) => { | |
// convertimos la palabra buscada en minúsculas | |
const searchedTerm = searchTerm.toLowerCase(); | |
// filtramos una copia de la lista original de comidas para devolver únicamente las comidas cuyo nombre (en minúsculas también) corresponden al término de búsqueda | |
const filteredList = [...this.state.foodsList].filter( foodObj => { | |
return foodObj.name.toLowerCase().includes(searchedTerm); | |
}) | |
// actualizamos la lista de comidas filtradas (la que vamos a mostrar en el render()) | |
this.setState({filteredFoodsList: filteredList}) | |
} | |
// ITERACIÓN 5 Y 6 (BONUS) | |
addTodaysFood = (foodObj) => { | |
// creamos una copia del array de las comidas de hoy | |
let todaysFoodsCopy = [...this.state.todaysFood] | |
// comprobamos si la comida a añadir ya se encuentra en el array todaysFoodsCopy | |
let found = todaysFoodsCopy.find(food => food.name === foodObj.name); | |
// calculamos las calorías totales según qué cantidad de la comida se ha añadido | |
foodObj.calories *= foodObj.quantity; | |
if(found) { | |
// si la comida ya existe en el array de comidas de hoy, le sumamos al objeto comida la cantidad y calorías que se añadieron nuevamente | |
found.quantity += foodObj.quantity; | |
found.calories += foodObj.calories; | |
} else { | |
// sino, añadimos el objeto foodObj al array todaysFoodsCopy | |
todaysFoodsCopy.push(foodObj); | |
} | |
// calculamos el total de calorías de todas las comidas de hoy | |
const totalCalories = todaysFoodsCopy.reduce( | |
(acc, val) => acc + val.calories, 0 | |
); | |
// actualizamos el state con la lista actualizada de comidas de hoy y el nuevo total de calorías | |
this.setState({ todaysFood: todaysFoodsCopy, totalCalories }); | |
} | |
// ITERACIÓN 7 (BONUS) | |
deleteFood (index) { | |
// creamos una copia del array de las comidas de hoy | |
const todaysFoodsCopy = [...this.state.todaysFood] | |
// sacamos la comida que corresponde al index recibido del array (copia) de comidas | |
todaysFoodsCopy.splice(index, 1); | |
// volvemos a calcular el total de calorías | |
const totalCalories = todaysFoodsCopy.reduce( | |
(acc, val) => acc + val.calories, 0 | |
); | |
// actualizamos la lista de comidas con el array copia y el nuevo total de calorías después de eliminar la comida | |
this.setState({ todaysFood: todaysFoodsCopy, totalCalories }); | |
} | |
render() { | |
return ( | |
<div className="container"> | |
<div className="section"> | |
<h1 className="title">IronNutrition</h1> | |
<Search searchFoods={this.filterFoods} /> | |
<div id="add-section"> | |
<button className="button" onClick={this.toggleForm}>Add new food</button> | |
{ this.state.showForm | |
? <AddNewFood addFood={this.addFood} /> | |
: null | |
} | |
</div> | |
<div className="columns"> | |
<div className="column"> | |
{this.state.filteredFoodsList.map( (food, index) => { | |
food.id = shortid.generate(); | |
return ( | |
<FoodBox | |
key={index} | |
food={food} | |
updateTodaysFood={this.addTodaysFood} | |
/> | |
) | |
})} | |
</div> | |
</div> | |
</div> | |
<div className="section"> | |
<h2 id="todays-food-title">Today's foods</h2> | |
{/* comprobamos que el array todaysFood contiene algo | |
> si es el caso, mostramos lo que hay en el array todaysFood | |
> sino, no mostramos nada (null) */} | |
{this.state.todaysFood.length > 0 | |
? | |
(<div> | |
<ul> | |
{this.state.todaysFood.map( (oneTodaysFood, index) => { | |
oneTodaysFood.id = shortid.generate(); | |
return <TodaysFood | |
key={oneTodaysFood.id} | |
{...oneTodaysFood} | |
deleteFood={() => this.deleteFood(index)} | |
/> | |
})} | |
</ul> | |
<p>Total calories: {this.state.totalCalories}</p> | |
</div>) | |
: null | |
} | |
</div> | |
</div> | |
); | |
} | |
} | |
export default FoodList; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Search.js | |
import React, { Component } from 'react'; | |
class Search extends Component { | |
state = { | |
search: "" | |
} | |
handleChange = e => { | |
const { name, value } = e.target; | |
this.setState({[name]: value}) | |
// invocar la función creada en FoodList.js que filtra la lista de comidas según la búsqueda | |
this.props.searchFoods(value); | |
} | |
render() { | |
return ( | |
<div> | |
<input | |
type="text" | |
className="input search-bar" | |
name="search" | |
placeholder="Search" | |
value={this.state.search} | |
onChange={this.handleChange} | |
/> | |
</div> | |
) | |
} | |
} | |
export default Search; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/TodaysFood.js | |
import React from 'react' | |
const TodaysFood = (props) => { | |
const { name, calories, quantity, deleteFood } = props; | |
return ( | |
<li> | |
<p>{quantity} {name} = {calories} cal</p> | |
<button className="button is-link" onClick={deleteFood}>Delete</button> | |
</li> | |
) | |
} | |
export default TodaysFood; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment