Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10,138 changes: 10,138 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
}
92 changes: 75 additions & 17 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,28 +1,86 @@
.App {
text-align: center;

* {
margin: 0;
padding: 0;
}

.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
body {
font-family: sans-serif;
}


input[type="text"], input[type="submit"], button {
appearance: none;
padding: 0.6rem 1rem;
font-size: 1rem;
outline: none;
border: 2px solid #ee4e7a;
border-radius: 4px;
margin-right: 0.4rem;
}

.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
input[type="submit"], button {
background-color: #ee4e7a;
color: #fff;
cursor: pointer;
}

.App-title {
font-size: 1.5em;
.error-message {
display: flex;
justify-content: center;
align-items: center;
min-height: 600px;
font-size: 1.4rem;
color: #444;
}

.App-intro {
font-size: large;
.search {
display: flex;
justify-content: center;
align-items: center;
margin: 2rem 0;
}

@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
.list {
max-width: 800px;
margin: 0 auto;
}

.list ul {
list-style: none;
}

.list li {
border: 1px solid #eee;
padding: 1rem 1.6rem;
display: flex;
justify-content: space-between;
align-items: center;
color: #444;
}

.list--title {
flex: 2;
display: flex;
align-items: center;
margin-right: 1rem;
}

.list li a {
color: #444;
}

.list li img {
width: 80px;
height: 80px;
margin-right: 1rem;
}


.list span {
/*flex: 1;*/
background-color: #eee;
color: #ee4e7a;
padding: 0.2rem 0.4rem;
border-radius: 4px;
}
112 changes: 104 additions & 8 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,114 @@
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import { RecipeList } from './components/RecipeList';
import { SearchBar } from './components/SearchBar';

import { APP_ID, APP_KEY } from './config/';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice job, moving keys into a new file


const ErrorMessage = ({message, isLoading}) => {
return (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to write my functions without returns if I can get away with it, like here!

<div className="error-message">
{
isLoading ? "Loading recipes..." : message
}
</div>
);
};

class App extends Component {
constructor(){
super();
this.state = {
searchTerm: '',
recipes: {
fetchedList: [],
errorMessage: ''
},
isLoading: false
}
}

updateSearchTerm = (e) => {
this.setState({
searchTerm: e.target.value
});
}

onClickSearch = (e) => {
e.preventDefault();
this.fetchRecipes(this.state.searchTerm);
}

fetchRecipes = (searchTerm) => {
const BASE_ENDPOINT = `https://api.edamam.com/search?q=${searchTerm}&app_id=${APP_ID}&app_key=${APP_KEY}`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would even consider moving the BASE_ENDPOINT into it's own file.


this.setState({
isLoading: true
});

fetch(BASE_ENDPOINT)
.then(res => res.json())
.then(recipes => {
if(recipes.hits.length > 0) {
const data = recipes.hits.map(({recipe}) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice use of map!

Remember that passing in your adaption function is good for code reuse. Also, you don't need Object.assign here, but I like that you're making sure your being immutible.

You could do:

return {
 name: recipe.label,
 calories: Math.floor(recipe.calories),
 image: recipe.image,
 url: recipe.url
}

which returns a new object (without mutations)

return Object.assign({}, {
name: recipe.label,
calories: Math.floor(recipe.calories),
image: recipe.image,
url: recipe.url
});
});
this.setState({
searchTerm: '',
recipes: {
fetchedList: data,
errorMessage: []
},
isLoading: false
});
}
else {
this.setState({
recipes: {
fetchedList: [],
errorMessage: `Sorry. We cannot find recipes of ${this.state.searchTerm}. Please search for another food item.`
},
isLoading: false
});
}
})
.catch(err => {
this.setState({
recipes: {
fetchedList: [],
errorMessage: 'Oops...Failed to load recipes. Please let us know what went wrong!'
},
isLoading: false
});
});
};

componentDidMount(){
this.fetchRecipes('cake');
}

render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<SearchBar
inputValue={this.state.searchTerm}
updateSearchTerm={this.updateSearchTerm}
onClickSearch={this.onClickSearch}
/>
{
this.state.recipes.fetchedList.length > 0 ?
<RecipeList recipes={this.state.recipes.fetchedList} /> :
<ErrorMessage
isLoading={this.state.isLoading}
message={this.state.recipes.errorMessage}
/>
}
</div>
);
}
Expand Down
21 changes: 21 additions & 0 deletions src/components/RecipeList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

export const RecipeList = ({recipes}) => {
return(
<ul className="list">
{
recipes.map(({name, calories, url, image}, idx) => {
return (
<li key={idx}>
<div className="list--title">
<img src={image} />
<h3><a href={url}>{name}</a></h3>
</div>
<span>{calories} kcals</span>
</li>
)
})
}
</ul>
);
};
14 changes: 14 additions & 0 deletions src/components/SearchBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

export const SearchBar = ({inputValue, onClickSearch, updateSearchTerm}) => {
return (
<form className="search" onSubmit={onClickSearch}>
<input
type="text"
value={inputValue}
onChange={updateSearchTerm}
/>
<input type="submit" value="Search"/>
</form>
);
};
2 changes: 2 additions & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const APP_ID = '9f1497b3';
export const APP_KEY = 'd489d7c7d4bec18f15a54e7467e0e936';
5 changes: 0 additions & 5 deletions src/index.css

This file was deleted.

1 change: 0 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

Expand Down
7 changes: 0 additions & 7 deletions src/logo.svg

This file was deleted.