contact page
10
README.md
|
|
@ -52,6 +52,7 @@ Free meal planner for cooks short on ideas! (like me …)
|
|||
- Cocktail selection
|
||||
- Create a profile and save your favourite meals
|
||||
- Notation system: know what are the most loved meals
|
||||
- Share recipe with your friends and family
|
||||
- Suggestions based on what your personal taste
|
||||
- Recipes in Video
|
||||
- Get a full menu (Starter, Main, Dessert + Cocktail)
|
||||
|
|
@ -84,14 +85,17 @@ Free meal planner for cooks short on ideas! (like me …)
|
|||
|
||||
- Progressive Web App
|
||||
- User Interface Enhancement
|
||||
- Contact form
|
||||
|
||||
## TO DO
|
||||
|
||||
- add sidenav on mobile
|
||||
- accounts v2
|
||||
- send message after contact form validation (confirm to sender and msg+info to admin)
|
||||
- code cleanup (props and refactoring)
|
||||
- put a preloader
|
||||
- redirect after failed fetch request: (history.push('/path'), or write handleFetchResponse function)
|
||||
- https://stackoverflow.com/questions/45089386/what-is-the-best-way-to-redirect-a-page-using-react-router
|
||||
- https://www.henriksommerfeld.se/error-handling-with-fetch/
|
||||
- Use ErrorBoundaries component ?
|
||||
- add sidenav on mobile
|
||||
- accounts v2
|
||||
- contact form (with validation)
|
||||
- Back to top button
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { MealPage } from "./pages/Meal";
|
|||
import { SearchPage } from "./pages/Search";
|
||||
import { CategoryListPage } from "./pages/CategoryList";
|
||||
import { CategoryPage } from "./pages/Category";
|
||||
import { ContactPage } from "./pages/Contact";
|
||||
import { NotFoundPage } from "./pages/NotFound";
|
||||
import { Navbar } from "./components/Navbar";
|
||||
import { SearchBar } from "./components/SearchBar";
|
||||
|
|
@ -137,6 +138,9 @@ export const App = () => {
|
|||
searchResults={searchResults}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/contact">
|
||||
<ContactPage />
|
||||
</Route>
|
||||
<Route path="/404">
|
||||
<NotFoundPage handleClick={getRandomMeal} />
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const CategoryEntry = props => {
|
|||
</div>
|
||||
<div className="card-stacked">
|
||||
<div className="card-content black-text">
|
||||
<h4 className="logo">{strCategory}</h4>
|
||||
<h2 className="logo">{strCategory}</h2>
|
||||
{/* <p>{strCategoryDescription}</p> */}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
98
src/components/ContactForm.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
export const ContactForm = ({ handleSubmit }) => {
|
||||
const fields = ["firstname", "lastname", "email", "phone", "message"];
|
||||
|
||||
// const [firstName, setFirstName] = useState("");
|
||||
|
||||
const onSubmit = ev => {
|
||||
ev.preventDefault();
|
||||
handleSubmit(true);
|
||||
};
|
||||
|
||||
// const handleChange = ev => {
|
||||
// const { value } = ev.target;
|
||||
// setFirstName(value);
|
||||
// };
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="row">
|
||||
<div className="col s12">
|
||||
<div className="col s12 m6">
|
||||
<ContactFormInput id="First Name" />
|
||||
</div>
|
||||
<div className="col s12 m6">
|
||||
<ContactFormInput id="Last Name" />
|
||||
</div>
|
||||
<div className="col s12 m6">
|
||||
<ContactFormInput
|
||||
id="Email"
|
||||
type="email"
|
||||
placeholder="jd@mail.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="col s12 m6">
|
||||
<ContactFormInput id="Phone" placeholder="0123456789" />
|
||||
</div>
|
||||
<div className="col s12">
|
||||
<ContactFormTextArea id="Message" />
|
||||
<ContactFormSubmit text="Send Message" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const ContactFormInput = ({ id, type = "text" }) => {
|
||||
return (
|
||||
<div className="input-field">
|
||||
{/* <i class="material-icons prefix">account_circle</i> */}
|
||||
<label for={id}>{id}</label>
|
||||
<input
|
||||
className="validate"
|
||||
type={type}
|
||||
id={id}
|
||||
name={id}
|
||||
// value={value}
|
||||
// onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ContactFormTextArea = ({ id }) => {
|
||||
return (
|
||||
<div className="input-field">
|
||||
<label for={id}>{id}</label>
|
||||
<textarea
|
||||
className="materialize-textarea validate"
|
||||
rows="12"
|
||||
// cols="50"
|
||||
name={id}
|
||||
// value={value}
|
||||
// onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ContactFormSubmit = ({ text }) => {
|
||||
return (
|
||||
<button
|
||||
className="waves-effect waves-light btn"
|
||||
type="submit"
|
||||
name="submit"
|
||||
>
|
||||
<i class="material-icons right">send</i> {text}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
// {fields.map(field => (
|
||||
// <ContactFormInput
|
||||
// value={field}
|
||||
// placeholder={field}
|
||||
// // onChange={handleChange}
|
||||
// />
|
||||
// ))}
|
||||
|
|
@ -4,7 +4,7 @@ import { GitHubLink } from "./GitHubLink";
|
|||
import { FooterLink } from "./FooterLink";
|
||||
|
||||
export const Footer = () => {
|
||||
const links = ["categories", "random"];
|
||||
const links = ["categories", "random", "contact"];
|
||||
|
||||
return (
|
||||
<footer className="page-footer">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const MealPresentation = props => {
|
|||
return (
|
||||
<div className="row">
|
||||
<div className="col s12">
|
||||
<div className="card blue-grey darken-1">
|
||||
<div className="card teal darken-1">
|
||||
<div className="card-content white-text">
|
||||
<span className="card-title">{mealName}</span>
|
||||
<img className="responsive-img" src={imgAddress} alt={mealName} />
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import React from "react";
|
|||
import { Logo } from "./Logo";
|
||||
|
||||
import { RandomButton } from "./RandomButton";
|
||||
import { Link } from "react-router-dom";
|
||||
import { FooterLink } from "./FooterLink";
|
||||
|
||||
export const Navbar = props => {
|
||||
const links = ["categories", "contact"];
|
||||
return (
|
||||
<div className="row">
|
||||
<nav>
|
||||
|
|
@ -12,9 +13,10 @@ export const Navbar = props => {
|
|||
<div className="container">
|
||||
<Logo />
|
||||
<ul id="nav-mobile" className="right hide-on-med-and-down">
|
||||
<li>
|
||||
<Link to="/categories">Categories</Link>
|
||||
</li>
|
||||
{links.map((link, i) => (
|
||||
<FooterLink i={i} link={link} />
|
||||
))}
|
||||
|
||||
<li>
|
||||
<RandomButton
|
||||
handleClick={props.handleClick}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
1
src/images/mail_sent.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg id="fd577a60-d552-4fe8-bffb-d9cccd3352c0" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="788.38181" height="719" viewBox="0 0 788.38181 719"><defs><linearGradient id="aeee3e51-5d06-43ae-b234-e7a12d326f50" x1="365" y1="605" x2="365" y2="286" gradientUnits="userSpaceOnUse"><stop offset="0" stop-opacity="0.12"/><stop offset="0.55135" stop-opacity="0.09"/><stop offset="1" stop-opacity="0.02"/></linearGradient><linearGradient id="acb63a3b-00bb-44a8-8877-bb0607d49a63" x1="1117.61899" y1="-43.05793" x2="1117.61899" y2="-102.40539" gradientTransform="matrix(-0.64881, 0.76095, 0.76095, 0.64881, 817.40491, -656.85567)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.53514" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient></defs><title>Mail sent</title><polygon points="125 410 0 268 374 0 748 268 621 410 125 410" fill="#ee6e73"/><polygon points="125 410 0 268 374 0 748 268 621 410 125 410" fill="#514abf"/><rect y="286" width="730" height="319" fill="#ee6e73"/><rect y="286" width="730" height="319" fill="url(#aeee3e51-5d06-43ae-b234-e7a12d326f50)"/><polygon points="748 719 0 719 0 268 374 494 748 268 748 719" fill="#ee6e73"/><polygon points="652.582 116.818 573.252 55.99 363.113 330.042 246.402 240.55 185.573 319.879 341.938 439.598 341.938 439.598 382.036 469.649 652.582 116.818" fill="#3ad29f"/><polygon points="3.153 166.882 0.179 168.334 0.521 167.726 0.26 167.81 0.626 167.539 40.653 96.307 61.114 113.753 78.667 130.004 73.627 132.465 79.293 142.459 3.153 166.882" fill="url(#acb63a3b-00bb-44a8-8877-bb0607d49a63)"/><polygon points="59.087 115.617 75.131 130.493 2.411 166.646 41.11 117.046 59.087 115.617" fill="#ee6e73"/><polygon points="59.087 115.617 75.131 130.493 2.411 166.646 41.11 117.046 59.087 115.617" opacity="0.2"/><polygon points="40.373 99.66 2.411 166.646 59.087 115.617 40.373 99.66" fill="#ee6e73"/><polygon points="75.599 142.01 2.489 166.16 63.604 120.539 75.599 142.01" fill="#ee6e73"/><polygon points="686.959 38.73 670.942 66.499 788.382 85.622 712.742 32.949 686.959 38.73" fill="#ee6e73"/><polygon points="686.959 38.73 670.942 66.499 788.382 85.622 712.742 32.949 686.959 38.73" opacity="0.2"/><polygon points="706.247 8.293 788.382 85.622 686.959 38.73 706.247 8.293" fill="#ee6e73"/><polygon points="675.274 82.821 788.062 84.976 682.769 47.576 675.274 82.821" fill="#ee6e73"/></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
23
src/pages/Contact.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React, { useState } from "react";
|
||||
import { ContactForm } from "../components/ContactForm";
|
||||
|
||||
export const ContactPage = props => {
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
|
||||
return isSubmitted ? (
|
||||
<div className="container center-align">
|
||||
<img
|
||||
className="responsive-img"
|
||||
src={require("../images/mail_sent.svg")}
|
||||
alt="mail_sent"
|
||||
width="30%"
|
||||
/>
|
||||
<h4>Thank you for your message</h4>
|
||||
</div>
|
||||
) : (
|
||||
<div className="container">
|
||||
<h1 className="logo">Contact Us</h1>
|
||||
<ContactForm handleSubmit={setIsSubmitted} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||