refactoring. Add MainLayout and MainRouter

This commit is contained in:
Ruidy Nemausat 2020-04-15 09:34:41 +02:00
parent 47c9d5f4d9
commit 2a6f843d36
23 changed files with 309 additions and 249 deletions

View file

@ -1,11 +1,14 @@
# TO DO # TO DO
- [ ] send message after contact form validation (confirm to sender and msg+info to admin) - [ ] send message after contact form validation (confirm to sender and msg+info to admin)
- [ ] Local storage of prefeernces - [ ] Local storage of preferences
- [ ] Firebase - [ ] Firebase firestore and functions
- [ ] Breadcrumb - [ ] Breadcrumb
- [ ] Cookie bar - [ ] Cookie bar
- [ ] code cleanup (props and refactoring) - [ ] code cleanup (props and refactoring)
- [ ] Back to top button - [ ] Back to top button
- [ ] Close Sidebar at outside click - [ ] Close Sidebar at outside click
- [ ] Take a look at some components [here](http://react-materialize.github.io/react-materialize/?path=/story/css-grid--default) - [ ] Take a look at some components [here](http://react-materialize.github.io/react-materialize/?path=/story/css-grid--default)
- [ ] Decoupled application and data layers. Abstract such that the front end does not know where the data comes from.
- [ ] Create PageLayout component
- [ ] Use Css-in-Js

View file

@ -1,24 +1,15 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Router } from "./utils/router"; import { Router } from "./utils/router";
import { Switch, Route, Redirect } from "react-router-dom";
import { PreLoader } from "./components/PreLoader";
import { useAuth0 } from "./utils/auth0-spa"; import { useAuth0 } from "./utils/auth0-spa";
import { SearchController } from "./controllers/SearchController";
import { ContactPage } from "./pages/Contact";
import { NotFoundPage } from "./pages/NotFoundPage";
import { Navbar } from "./components/Navbar";
import { SearchBar } from "./components/SearchBar";
import { Footer } from "./components/Footer";
import { getData } from "./utils/methods"; import { getData } from "./utils/methods";
import history from "./utils/history"; import history from "./utils/history";
import { ProfileController } from "./controllers/ProfileController";
import { PrivateRoute } from "./components/PrivateRoute";
import { PreLoader } from "./components/PreLoader";
import { SideNav } from "./components/SideNav";
import "./index.css"; import "./index.css";
import { HomeController } from "./controllers/HomeController"; import MainLayout from "./layouts/MainLayout";
import { MealController } from "./controllers/MealController"; import MainRouter from "./controllers/MainRouter";
import { CategoryListController } from "./controllers/CategoryListController";
import { CategoryController } from "./controllers/CategoryController";
export const App = () => { export const App = () => {
const { loading } = useAuth0(); const { loading } = useAuth0();
@ -85,6 +76,7 @@ export const App = () => {
}, },
], ],
}; };
const [meal, setMeal] = useState(mealDef); const [meal, setMeal] = useState(mealDef);
const getMeal = (id) => { const getMeal = (id) => {
@ -108,114 +100,33 @@ export const App = () => {
const buttonUrl = "/random"; const buttonUrl = "/random";
const [showNav, setShowNav] = useState(false);
const openNavClick = (ev) => {
ev.preventDefault();
setShowNav(true);
document.addEventListener("keydown", handleEscKey);
// document.addEventListener("click", handleOutsideClick);
};
const closeNavClick = (ev) => {
ev.preventDefault();
setShowNav(false);
document.removeEventListener("keydown", handleEscKey);
};
const handleEscKey = (ev) => {
if (ev.key === "Escape") {
setShowNav(false);
}
};
// const handleOutsideClick = ev => {
// console.log(ev);
// };
const links = ["categories", "contact"];
return loading ? ( return loading ? (
<div className="container center-align valign-wrapper"> <div className="container center-align valign-wrapper">
<PreLoader /> <PreLoader />
</div> </div>
) : ( ) : (
<> <Router history={history}>
<Router history={history}> <MainLayout
<header> buttonUrl={buttonUrl}
<Navbar meal={meal}
handleClick={getRandomMeal} getMeal={getMeal}
buttonUrl={buttonUrl} getRandomMeal={getRandomMeal}
openNavClick={openNavClick} searchString={searchString}
links={links} searchResults={searchResults}
/> setSearchResults={setSearchResults}
handleChange={handleChange}
<SearchBar setSearchString={setSearchString}
searchString={searchString} getSearchResults={getSearchResults}
setSearchString={setSearchString} >
handleChange={handleChange} <MainRouter
onSubmit={getSearchResults} buttonUrl={buttonUrl}
setSearchResults={setSearchResults} meal={meal}
/> getMeal={getMeal}
<SideNav getRandomMeal={getRandomMeal}
showNav={showNav} searchString={searchString}
closeNavClick={closeNavClick} searchResults={searchResults}
links={links} />
buttonUrl={buttonUrl} </MainLayout>
/> </Router>
</header>
<Switch>
<Route exact path="/">
<HomeController buttonUrl={buttonUrl} />
</Route>
<PrivateRoute exact path="/profile">
<ProfileController />
</PrivateRoute>
<Route exact path={buttonUrl}>
<MealController
meal={meal}
getMeal={getMeal}
getRandomMeal={getRandomMeal}
/>
</Route>
<Route exact path="/categories">
<CategoryListController />
</Route>
<Route path="/categories/:strCategory/">
<CategoryController />
</Route>
<Route exact path="/search">
<SearchController
searchString={searchString}
searchResults={searchResults}
/>
</Route>
<Route path="/contact">
<ContactPage />
</Route>
<Route path="/404">
<NotFoundPage handleClick={getRandomMeal} />
</Route>
<Route path="/:id">
<MealController
meal={meal}
getMeal={getMeal}
getRandomMeal={getRandomMeal}
/>
</Route>
<Route path="*">
<Redirect to="/404" />
</Route>
</Switch>
<Footer />
</Router>
</>
); );
}; };

View file

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export const CardEntry = props => { export const CardEntry = ({ item }) => {
const { idMeal, strMeal, strMealThumb } = props.item; const { idMeal, strMeal, strMealThumb } = item;
return ( return (
<Link to={`${idMeal}`}> <Link to={`${idMeal}`}>
<li> <li>

View file

@ -2,14 +2,14 @@ import React from "react";
import { Link, useRouteMatch } from "react-router-dom"; import { Link, useRouteMatch } from "react-router-dom";
const CategoryEntry = ({ category }) => { const CategoryEntry = ({ category }) => {
const { url } = useRouteMatch();
const { const {
strCategory, strCategory,
strCategoryThumb strCategoryThumb,
// strCategoryDescription // strCategoryDescription
} = category; } = category;
const { url } = useRouteMatch();
return ( return (
// <CardEntry item={meal} /> // <CardEntry item={meal} />
<Link to={`${url}/${strCategory}`}> <Link to={`${url}/${strCategory}`}>

View file

@ -3,9 +3,7 @@ import { CopyrightText } from "./CopyrightText";
import { GitHubLink } from "./GitHubLink"; import { GitHubLink } from "./GitHubLink";
import { FooterLink } from "./FooterLink"; import { FooterLink } from "./FooterLink";
export const Footer = () => { export const Footer = ({ links }) => {
const links = ["categories", "random", "contact"];
return ( return (
<footer className="page-footer"> <footer className="page-footer">
<div className="row"> <div className="row">

View file

@ -1,7 +1,6 @@
import React from "react"; import React from "react";
export const IngredientList = props => { export const IngredientList = ({ ingredients }) => {
const { ingredients } = props;
return ( return (
<div className="ingredientList"> <div className="ingredientList">
<table className="striped highlight responsive-table"> <table className="striped highlight responsive-table">

View file

@ -3,17 +3,15 @@ import { Link } from "react-router-dom";
export const Logo = () => { export const Logo = () => {
return ( return (
<> <Link to="/" className="brand-logo">
<Link to="/" className="brand-logo"> <img
<img // className="responsive-img"
// className="responsive-img" src="/logo192.png"
src="/logo192.png" alt="chef's logo"
alt="chef's logo" height="30px"
height="30px" style={{ position: "relative", top: "5px" }}
style={{ position: "relative", top: "5px" }} />
/> <span className="logo orange-text text-accent-4">Chef's</span>
<span className="logo orange-text text-accent-4">Chef's</span> </Link>
</Link>
</>
); );
}; };

View file

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export const MealPresentation = (props) => { export const MealPresentation = ({ meal }) => {
const { const {
mealName, mealName,
imgAddress, imgAddress,
@ -10,7 +10,7 @@ export const MealPresentation = (props) => {
mealArea, mealArea,
isFav, isFav,
setIsFav, setIsFav,
} = props.meal; } = meal;
return ( return (
<div className="row"> <div className="row">

View file

@ -1,11 +1,11 @@
import React from "react"; import React from "react";
export const Recipe = props => { export const Recipe = ({ recipe }) => {
return ( return (
<div className="recipe"> <div className="recipe">
<div className="divider"></div> <div className="divider"></div>
<h3>Instructions</h3> <h3>Instructions</h3>
<p className="flow-text">{props.recipe}</p> <p className="flow-text">{recipe}</p>
</div> </div>
); );
}; };

View file

@ -6,12 +6,13 @@ export const SearchBar = ({
setSearchString, setSearchString,
setSearchResults, setSearchResults,
handleChange, handleChange,
onSubmit onSubmit,
}) => { }) => {
const clearSearchBar = () => { const clearSearchBar = () => {
setSearchString(""); setSearchString("");
setSearchResults({ meals: [] }); setSearchResults({ meals: [] });
}; };
return ( return (
<div className="section"> <div className="section">
<div className="container"> <div className="container">

View file

@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import { CardEntry } from "./CardEntry"; import { CardEntry } from "./CardEntry";
export const SearchResult = props => { export const SearchResult = ({ meal }) => {
const { meal } = props;
return <CardEntry item={meal} />; return <CardEntry item={meal} />;
}; };

View file

@ -11,94 +11,92 @@ export const SideNav = ({
closeNavClick, closeNavClick,
links, links,
buttonUrl, buttonUrl,
handleClick handleClick,
}) => { }) => {
const { isAuthenticated, user } = useAuth0(); const { isAuthenticated, user } = useAuth0();
let transformStyle = { let transformStyle = {
transform: showNav ? "translateX(0%)" : "translateX(-105%)", transform: showNav ? "translateX(0%)" : "translateX(-105%)",
transition: "0.5s" transition: "0.5s",
}; };
return ( return (
<> <ul id="slide-out" className="sidenav" style={transformStyle}>
<ul id="slide-out" className="sidenav" style={transformStyle}> <li>
<li> <div className="user-view" style={{ height: "30vh" }}>
<div className="user-view" style={{ height: "30vh" }}> <div className="background">
<div className="background"> <img
<img // className="responsive-img"
// className="responsive-img" style={{
style={{ position: "fixed" /* Sit on top of the page content */,
position: "fixed" /* Sit on top of the page content */, width: "100%" /* Full width (cover the whole page) */,
width: "100%" /* Full width (cover the whole page) */, height: "30vh" /* Full width (cover the whole page) */,
height: "30vh" /* Full width (cover the whole page) */, top: "0",
top: "0", left: "0",
left: "0", right: "0",
right: "0", bottom: "0",
bottom: "0", backgroundColor:
backgroundColor: "rgba(0,0,0,0.5)" /* Black background with opacity */,
"rgba(0,0,0,0.5)" /* Black background with opacity */, zIndex:
zIndex: "2" /* Specify a stack order in case you're using a different order for other elements */,
"2" /* Specify a stack order in case you're using a different order for other elements */ // cursor: "pointer" /* Add a pointer on hover */
// cursor: "pointer" /* Add a pointer on hover */ }}
}} src={require("../images/special_event.svg")}
src={require("../images/special_event.svg")} alt="sidenav_background"
alt="sidenav_background" />
/>
</div>
<i className="material-icons right" onClick={closeNavClick}>
close
</i>
{isAuthenticated ? (
<Link to="/profile">
<img className="circle" src={user.picture} alt="user_avatar" />
<span className="white-text name">{user.name}</span>
<span className="white-text email">{user.email}</span>
</Link>
) : (
// <Link to="/profile">
<img
className="circle"
src={require("../images/chef.svg")}
alt="user_avatar"
/>
// </Link>
)}
</div> </div>
</li>
<li> <i className="material-icons right" onClick={closeNavClick}>
<RandomButton close
handleClick={handleClick} </i>
url={buttonUrl}
size="small"
color="orange darken-2"
/>
</li>
<li>
<Link to="#">
{!isAuthenticated ? (
<LogInButton color="orange lighten-1" />
) : (
<LogOutButton />
)}
</Link>
</li>
<li> {isAuthenticated ? (
<div className="divider"></div> <Link to="/profile">
</li> <img className="circle" src={user.picture} alt="user_avatar" />
<li> <span className="white-text name">{user.name}</span>
<Link to="#" className="subheader"> <span className="white-text email">{user.email}</span>
Navigation </Link>
</Link> ) : (
</li> // <Link to="/profile">
{links.map((link, i) => ( <img
<FooterLink key={i} link={link} /> className="circle"
))} src={require("../images/chef.svg")}
{isAuthenticated && <FooterLink link="profile" />} alt="user_avatar"
</ul> />
</> // </Link>
)}
</div>
</li>
<li>
<RandomButton
handleClick={handleClick}
url={buttonUrl}
size="small"
color="orange darken-2"
/>
</li>
<li>
<Link to="#">
{!isAuthenticated ? (
<LogInButton color="orange lighten-1" />
) : (
<LogOutButton />
)}
</Link>
</li>
<li>
<div className="divider"></div>
</li>
<li>
<Link to="#" className="subheader">
Navigation
</Link>
</li>
{links.map((link, i) => (
<FooterLink key={i} link={link} />
))}
{isAuthenticated && <FooterLink link="profile" />}
</ul>
); );
}; };

View file

@ -3,7 +3,7 @@ import { useParams, Redirect } from "react-router-dom";
import { CategoryPage } from "../pages/CategoryPage"; import { CategoryPage } from "../pages/CategoryPage";
import { getData } from "../utils/methods"; import { getData } from "../utils/methods";
export const CategoryController = props => { export const CategoryController = () => {
const [meals, setMeals] = useState({ meals: [] }); const [meals, setMeals] = useState({ meals: [] });
const { strCategory } = useParams(); const { strCategory } = useParams();
@ -17,13 +17,9 @@ export const CategoryController = props => {
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
return ( return meals.meals === null ? (
<> <Redirect to="/404" />
{meals.meals === null ? ( ) : (
<Redirect to="/404" /> <CategoryPage meals={meals} strCategory={strCategory} />
) : (
<CategoryPage meals={meals} strCategory={strCategory} />
)}
</>
); );
}; };

View file

@ -1,5 +1,4 @@
import React from "react"; import React from "react";
import { HomePage } from "../pages/HomePage"; import { HomePage } from "../pages/HomePage";
export const HomeController = ({ buttonUrl }) => { export const HomeController = ({ buttonUrl }) => {

View file

@ -0,0 +1,80 @@
import React from "react";
import { Switch, Route, Redirect } from "react-router-dom";
import { SearchController } from "../controllers/SearchController";
import { HomeController } from "../controllers/HomeController";
import { MealController } from "../controllers/MealController";
import { CategoryController } from "../controllers/CategoryController";
import { CategoryListController } from "../controllers/CategoryListController";
import { ProfileController } from "../controllers/ProfileController";
import { ContactPage } from "../pages/Contact";
import { NotFoundPage } from "../pages/NotFoundPage";
import { PrivateRoute } from "../components/PrivateRoute";
const MainRouter = ({
buttonUrl,
meal,
getMeal,
getRandomMeal,
searchString,
searchResults,
}) => {
return (
<Switch>
<Route exact path="/">
<HomeController buttonUrl={buttonUrl} />
</Route>
<PrivateRoute exact path="/profile">
<ProfileController />
</PrivateRoute>
<Route exact path={buttonUrl}>
<MealController
meal={meal}
getMeal={getMeal}
getRandomMeal={getRandomMeal}
/>
</Route>
<Route exact path="/categories">
<CategoryListController />
</Route>
<Route path="/categories/:strCategory/">
<CategoryController />
</Route>
<Route exact path="/search">
<SearchController
searchString={searchString}
searchResults={searchResults}
/>
</Route>
<Route path="/contact">
<ContactPage />
</Route>
<Route path="/404">
<NotFoundPage handleClick={getRandomMeal} />
</Route>
<Route path="/:id">
<MealController
meal={meal}
getMeal={getMeal}
getRandomMeal={getRandomMeal}
/>
</Route>
<Route path="*">
<Redirect to="/404" />
</Route>
</Switch>
);
};
export default MainRouter;

View file

@ -4,7 +4,6 @@ import { useAuth0 } from "../utils/auth0-spa";
import { MealPage } from "../pages/MealPage"; import { MealPage } from "../pages/MealPage";
import { NotFoundPage } from "../pages/NotFoundPage"; import { NotFoundPage } from "../pages/NotFoundPage";
import { useFirebase } from "../services/Firebase"; import { useFirebase } from "../services/Firebase";
// import Firebase from "../data/Firebase";
export const MealController = ({ meal, getMeal, getRandomMeal }) => { export const MealController = ({ meal, getMeal, getRandomMeal }) => {
const { user, isAuthenticated } = useAuth0(); const { user, isAuthenticated } = useAuth0();

View file

@ -1,5 +1,4 @@
import React from "react"; import React from "react";
import { SearchPage } from "../pages/SearchPage"; import { SearchPage } from "../pages/SearchPage";
export const SearchController = ({ searchString, searchResults }) => { export const SearchController = ({ searchString, searchResults }) => {

View file

@ -0,0 +1,75 @@
import React, { useState } from "react";
import { Navbar } from "../components/Navbar";
import { SearchBar } from "../components/SearchBar";
import { Footer } from "../components/Footer";
import { SideNav } from "../components/SideNav";
const MainLayout = ({
buttonUrl,
getRandomMeal,
handleChange,
searchString,
setSearchString,
getSearchResults,
setSearchResults,
children,
}) => {
const [showNav, setShowNav] = useState(false);
const links = ["categories", "contact"];
const footerLinks = [...links, "random"];
const openNavClick = (ev) => {
ev.preventDefault();
setShowNav(true);
document.addEventListener("keydown", handleEscKey);
// document.addEventListener("click", handleOutsideClick);
};
const closeNavClick = (ev) => {
ev.preventDefault();
setShowNav(false);
document.removeEventListener("keydown", handleEscKey);
};
const handleEscKey = (ev) => {
if (ev.key === "Escape") {
setShowNav(false);
}
};
// const handleOutsideClick = ev => {
// console.log(ev);
// };
return (
<>
<header>
<Navbar
handleClick={getRandomMeal}
buttonUrl={buttonUrl}
openNavClick={openNavClick}
links={links}
/>
<SearchBar
searchString={searchString}
setSearchString={setSearchString}
handleChange={handleChange}
onSubmit={getSearchResults}
setSearchResults={setSearchResults}
/>
<SideNav
showNav={showNav}
closeNavClick={closeNavClick}
links={links}
buttonUrl={buttonUrl}
/>
</header>
{children}
<Footer links={[...links, "random"]} />
</>
);
};
export default MainLayout;

View file

@ -26,7 +26,6 @@ export const CategoryPage = ({ meals, strCategory }) => {
))} ))}
</div> </div>
</ul> </ul>
}
</div> </div>
); );
}; };

View file

@ -1,5 +1,7 @@
import { createContext, useContext } from "react"; import { createContext, useContext } from "react";
// create a Firebase context to make state available anywhere in the App.
const FirebaseContext = createContext(null); const FirebaseContext = createContext(null);
export const useFirebase = () => useContext(FirebaseContext); export const useFirebase = () => useContext(FirebaseContext);
export default FirebaseContext; export default FirebaseContext;

View file

@ -13,6 +13,8 @@ const CONFIG = {
measurementId: config.measurementId, measurementId: config.measurementId,
}; };
// Firebase initializes the Application and provides method to interact with
// Firebase services as auth and firestore.
export default class Firebase { export default class Firebase {
constructor() { constructor() {
app.initializeApp(CONFIG); app.initializeApp(CONFIG);

View file

@ -1,3 +1,5 @@
// This file centralize all Firebase related exports
import Firebase from "./firebase"; import Firebase from "./firebase";
import FirebaseContext, { useFirebase } from "./context"; import FirebaseContext, { useFirebase } from "./context";

View file

@ -1,8 +1,8 @@
// This must be set on the server using express // This must be set on the server using or firebase functions
import { createTransport } from "nodemailer"; import { createTransport } from "nodemailer";
import { mailPassword } from "./secret"; import { mailAdress, mailPassword } from "./secret";
const myMail = "ruidy.nemausat@gmail.com"; const myMail = mailAdress;
const myPass = mailPassword; const myPass = mailPassword;
const handleMail = (mailTo, subject, text) => { const handleMail = (mailTo, subject, text) => {
@ -10,18 +10,18 @@ const handleMail = (mailTo, subject, text) => {
service: "gmail", service: "gmail",
auth: { auth: {
user: myMail, user: myMail,
pass: myPass pass: myPass,
} },
}); });
let mailOptions = { let mailOptions = {
from: myMail, from: myMail,
to: mailTo, to: mailTo,
subject: subject, subject: subject,
text: text text: text,
}; };
transporter.sendMail(mailOptions, function(error, info) { transporter.sendMail(mailOptions, function (error, info) {
if (error) { if (error) {
console.log(error); console.log(error);
} else { } else {