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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
14,021 changes: 14,021 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "challenge",
"version": "0.1.0",
"private": true,
"dependencies": {
"bulma": "^0.7.5",
"bulmaswatch": "^0.7.2",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"jest-fetch-mock": "^1.5.0",
"node-sass": "^4.12.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-on-visible": "^1.6.0",
"react-icons": "^3.2.2",
"react-redux": "^5.0.7",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-scripts": "3.0.1",
"react-test-renderer": "^16.3.1",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.5",
"redux-thunk": "^2.3.0",
"redux-saga": "^0.15.6",
"reselect": "^4.0.0",
"sinon": "^7.1.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file added public/favicon.ico
Binary file not shown.
15 changes: 15 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Buoy Chalenge App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div style="margin-top: 20px" id="root"></div>
</body>
</html>
15 changes: 15 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
35 changes: 35 additions & 0 deletions src/App.container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import AppRouter from './App';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { getList,
raiseError,
getDetail,
cleanDetail,
shouldGetIngredients,
selectDrink } from "./store/actions";

const mapStateToProps = (state) => {
return({
cocktails: state.app.cocktails,
selectedCocktail: state.app.selectedCocktail,
error: state.app.error,
fetching: state.app.fetching,
});
};

const dispatchActionsToProps = (dispatch) => {
return bindActionCreators(
{
getList,
raiseError,
getDetail,
cleanDetail,
shouldGetIngredients,
selectDrink,
},
dispatch
);
}

export default withRouter(connect(mapStateToProps, dispatchActionsToProps)(AppRouter));
61 changes: 61 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import ErrorBoundary from './shared/components/ErrorBoundary/ErrorBoundary';
import Guard from "./shared/components/Guard/guard";
import DetailSection from "./shared/components/DetailSection/DetailSection";
import List from "./shared/components/List/List";
import ListItem from "./shared/components/ListItem/ListItem";

const AppRouter = (props) => {
const [selectedCocktail, setSelectedCocktail] = React.useState({});

React.useEffect(() => {
props.getList();
}, [])

React.useEffect(() => {
setSelectedCocktail(props.selectedCocktail);
}, [props.selectedCocktail])

const { error,
raiseError,
getDetail,
cocktails,
fetching,
cleanDetail,
shouldGetIngredients,
selectDrink } = props;

const cocktailList = () => (
<List
comp={ListItem}
selectCocktail={selectDrink}
list={cocktails}
shouldGetIngredients={shouldGetIngredients}
/>
);

const cocktailDetail = () => (
<DetailSection
fetching={fetching}
cocktail={selectedCocktail}
getDetail={getDetail}
cleanDetail={cleanDetail}
/>
);

return (
<React.Fragment>
<ErrorBoundary error={error} raiseError={raiseError}>
<Switch>
<Route exact path='/' component={cocktailList} />
<Guard path="/cocktail/:id"
component={cocktailDetail}
isAuthenticated={true} />
</Switch>
</ErrorBoundary>
</React.Fragment>
);
}

export default AppRouter;
13 changes: 13 additions & 0 deletions src/constants/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const FETCH_LIST = "FETCH_LIST";
export const FETCH_LIST_SUCCESS = "FETCH_LIST_SUCCESS";
export const FETCH_LIST_ERROR = "FETCH_LIST_SUCCESS";

export const FETCH_DETAIL = "FETCH_DETAIL";
export const FETCH_DETAIL_SUCCESS = "FETCH_DETAIL_SUCCESS";
export const FETCH_DETAIL_ERROR = "FETCH_DETAIL_ERROR";
export const CLEAN_DETAIL = "CLEAN_DETAIL";

export const SELECT_DRINK = "SELECT_DRINK";
export const SHOULD_FETCH_DETAIL = "SHOULD_FETCH_DETAIL";

export const RAISE_ERROR = 'RAISE_ERROR';
11 changes: 11 additions & 0 deletions src/constants/endpoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const URLS = {
LIST_URL: 'https://www.thecocktaildb.com/api/json/v1/1/filter.php?g=Cocktail_glass',
DETAIL_URL: 'https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=',
// FURTHER API ENDPOINTS
};

export const METHOD = {
GET: 'GET',
POST: 'POST',
PUT: 'PUT',
};
18 changes: 18 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import './index.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import { BrowserRouter } from 'react-router-dom';
import AppRouter from './App.container';
import store from './store/store';

ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AppRouter/>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
16 changes: 16 additions & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import "node_modules/bulmaswatch/Solar/bulmaswatch";

body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
7 changes: 7 additions & 0 deletions src/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/setupTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });
global.fetch = require('jest-fetch-mock');
83 changes: 83 additions & 0 deletions src/shared/components/DetailSection/DetailSection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import { getObjProps } from '../../utils/utils';
import { FaLongArrowAltLeft } from 'react-icons/fa';
import './DetailSection.scss'
import Spinner from '../Spinner/Spinner';

export const DetailSection = (props) => {
const [fetching, setfetching] = React.useState(true);

const getObjPropsCount = () => Object.keys(props.cocktail).length;

React.useEffect(() => {
if (!(props.cocktail.idDrink) && props.match.params.id && !props.fetching) {
props.getDetail(props.match.params.id);
}
}, []);

React.useEffect(() => {
setfetching(props.fetching);
}, [props.fetching]);

const formatIngredients = () => {
const ingredients = getObjProps(props.cocktail, 'ingredient');
return (
<ul>
{ingredients.map((ing, i) => {
return (
<li key={i}>{props.cocktail[`strMeasure${i+1}`]} {props.cocktail[ing]}</li>
);
})}
</ul>
);
};

const goBack = () => {
props.cleanDetail();
props.history.push('/');
};

return (
<div>
{ fetching && !(getObjPropsCount() > 3) ? (
<Spinner message={'Loading ...'} />
) : (
<div>
<div className='level is-mobile'>
<div className='level-left' onClick={goBack}>
<FaLongArrowAltLeft />
</div>
<div className='level-right' style={{ margin: 'auto' }}>
<p className='title is-4 has-text-centered has-text-white has-text-weight-light'>
{props.cocktail.strDrink}
</p>
</div>
</div>
<div className='box has-background-white-ter level'>
<div className='level-left'>
<img className='level-item' src={props.cocktail.strDrinkThumb} />
</div>
<div className='level-right'>
<div className='level-item ingredients'>
{formatIngredients()}
</div>
<div className='level-item how-to has-text-left'>
<div>
<li>How to prepare</li>
</div>
<div>
<p>
{props.cocktail.strInstructions}
</p>
</div>
</div>
</div>
</div>
</div>
)}
</div>
)
}

export default withRouter(DetailSection);
41 changes: 41 additions & 0 deletions src/shared/components/DetailSection/DetailSection.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.box {
.level-left {
display: flex;
margin: auto;

img {
max-height: 350px;
margin: auto;
}
}
}

.level-right {
display: flex;
flex-direction: column;
}

.ingredients {
margin-right: auto !important;
padding-bottom: 20px;
}

.level {
margin-top: 45px;

.level-left {
font-size: 35px;
margin: 0px 0px 0px 15px;
}
}

.how-to {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-right: auto;

p {
margin-top: 15px;
}
}
Loading