style: update coding style

This commit is contained in:
Ruidy Nemausat 2020-07-09 15:01:16 +02:00
parent 9f6ba1863e
commit aab6ec0eea
98 changed files with 13529 additions and 17610 deletions

16069
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,25 @@
{ {
"name": "my-divjoy-project", "name": "nemo.immo",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"keywords": [ "keywords": [
"divjoy" "ReactJs"
], ],
"dependencies": { "dependencies": {
"@analytics/google-analytics": "0.2.0", "@analytics/google-analytics": "^0.4.0",
"@fortawesome/fontawesome-svg-core": "^1.2.25", "@fortawesome/fontawesome-svg-core": "^1.2.29",
"@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/free-solid-svg-icons": "^5.13.1",
"@fortawesome/react-fontawesome": "^0.1.7", "@fortawesome/react-fontawesome": "^0.1.11",
"analytics": "0.2.6", "analytics": "^0.5.2",
"bulma": "0.7.5", "bulma": "^0.9.0",
"firebase": "6.6.2", "firebase": "^7.15.5",
"node-sass": "4.12.0", "node-sass": "^4.14.1",
"query-string": "6.8.3", "query-string": "^6.13.1",
"react": "16.10.2", "react": "^16.13.1",
"react-dom": "16.10.2", "react-dom": "^16.13.1",
"react-rainbow-components": "^1.9.0", "react-rainbow-components": "^1.16.0",
"react-router-dom": "5.1.0", "react-router-dom": "^5.2.0",
"react-scripts": "3.2.0" "react-scripts": "^3.4.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
@ -41,6 +41,5 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
}, }
"proxy": "http://localhost:8080"
} }

View file

@ -1,6 +1,6 @@
{ {
"short_name": "React App", "short_name": "nemo.immo",
"name": "Create React App Sample", "name": "nemo.immo template",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",
@ -12,4 +12,4 @@
"display": "standalone", "display": "standalone",
"theme_color": "#000000", "theme_color": "#000000",
"background_color": "#ffffff" "background_color": "#ffffff"
} }

View file

@ -1,11 +1,15 @@
import React, { useState } from "react"; import React, { useState } from "react";
import FormStatus from "./FormStatus"; import FormStatus from "./FormStatus";
import FormField from "./FormField"; import FormField from "./FormField";
import SectionButton from "./SectionButton"; import SectionButton from "./SectionButton";
import { Link } from "./../util/router.js"; import { Link } from "./../util/router.js";
import * as ROUTES from "../global/routes";
import "./Auth.scss"; import "./Auth.scss";
function Auth(props) { const Auth = ({ mode, onSubmit, status, parentColor, buttonText }) => {
// State for all inputs // State for all inputs
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [pass, setPass] = useState(""); const [pass, setPass] = useState("");
@ -21,29 +25,29 @@ function Auth(props) {
let errors = []; let errors = [];
// Function for fetching error for a field // Function for fetching error for a field
const getError = field => { const getError = (field) => {
return errors.find(e => e.field === field); return errors.find((e) => e.field === field);
}; };
// Function to see if field is empty // Function to see if field is empty
const isEmpty = val => val.trim() === ""; const isEmpty = (val) => val.trim() === "";
// Add error if email empty // Add error if email empty
if (["signin", "signup", "forgotpass"].includes(props.mode)) { if (["signin", "signup", "forgotpass"].includes(mode)) {
if (isEmpty(email)) { if (isEmpty(email)) {
errors.push({ errors.push({
field: "email", field: "email",
message: "Please enter an email" message: "Please enter an email",
}); });
} }
} }
// Add error if password empty // Add error if password empty
if (["signin", "signup", "changepass"].includes(props.mode)) { if (["signin", "signup", "changepass"].includes(mode)) {
if (isEmpty(pass)) { if (isEmpty(pass)) {
errors.push({ errors.push({
field: "pass", field: "pass",
message: "Please enter a password" message: "Please enter a password",
}); });
} }
} }
@ -51,16 +55,16 @@ function Auth(props) {
// Add error if confirmPass empty or // Add error if confirmPass empty or
// if it doesn't match pass. // if it doesn't match pass.
// Only for signup and changepass views. // Only for signup and changepass views.
if (["signup", "changepass"].includes(props.mode)) { if (["signup", "changepass"].includes(mode)) {
if (isEmpty(confirmPass)) { if (isEmpty(confirmPass)) {
errors.push({ errors.push({
field: "confirmPass", field: "confirmPass",
message: "Please confirm password" message: "Please confirm password",
}); });
} else if (pass !== confirmPass) { } else if (pass !== confirmPass) {
errors.push({ errors.push({
field: "confirmPass", field: "confirmPass",
message: `This doesn't match your password` message: `This doesn't match your password`,
}); });
} }
} }
@ -72,10 +76,10 @@ function Auth(props) {
setShowErrors(true); setShowErrors(true);
} else { } else {
// Otherwise call onSubmit with email/pass // Otherwise call onSubmit with email/pass
if (props.onSubmit) { if (onSubmit) {
props.onSubmit({ onSubmit({
email, email,
pass pass,
}); });
} }
} }
@ -83,76 +87,72 @@ function Auth(props) {
return ( return (
<div className="Auth"> <div className="Auth">
{props.status && props.status.message && ( {status && status.message && (
<FormStatus type={props.status.type} message={props.status.message} /> <FormStatus type={status.type} message={status.message} />
)} )}
<form <form
onSubmit={e => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
handleSubmit(); handleSubmit();
}} }}
> >
{["signup", "signin", "forgotpass"].includes(props.mode) && ( {["signup", "signin", "forgotpass"].includes(mode) && (
<FormField <FormField
value={email} value={email}
type="email" type="email"
placeholder="Email" placeholder="Email"
error={showErrors && getError("email")} error={showErrors && getError("email")}
onChange={value => setEmail(value)} onChange={(value) => setEmail(value)}
/> />
)} )}
{["signup", "signin", "changepass"].includes(props.mode) && ( {["signup", "signin", "changepass"].includes(mode) && (
<FormField <FormField
value={pass} value={pass}
type="password" type="password"
placeholder="Password" placeholder="Password"
error={showErrors && getError("pass")} error={showErrors && getError("pass")}
onChange={value => setPass(value)} onChange={(value) => setPass(value)}
/> />
)} )}
{["signup", "changepass"].includes(props.mode) && ( {["signup", "changepass"].includes(mode) && (
<FormField <FormField
value={confirmPass} value={confirmPass}
type="password" type="password"
placeholder="Confirm Password" placeholder="Confirm Password"
error={showErrors && getError("confirmPass")} error={showErrors && getError("confirmPass")}
onChange={value => setConfirmPass(value)} onChange={(value) => setConfirmPass(value)}
/> />
)} )}
<div className="field"> <div className="field">
<p className="control "> <p className="control ">
<SectionButton <SectionButton
parentColor={props.parentColor} parentColor={parentColor}
size="medium" size="medium"
fullWidth={true} fullWidth={true}
state={ state={status && status.type === "pending" ? "loading" : "normal"}
props.status && props.status.type === "pending"
? "loading"
: "normal"
}
> >
{props.buttonText} {buttonText}
</SectionButton> </SectionButton>
</p> </p>
</div> </div>
{["signup", "signin"].includes(props.mode) && ( {["signup", "signin"].includes(mode) && (
<div className="Auth__bottom-link has-text-centered"> <div className="Auth__bottom-link has-text-centered">
{props.mode === "signup" && ( {mode === "signup" && (
<> <>
Have an account already? Have an account already?
<Link to="/signin">Sign in</Link> <Link to={ROUTES.SIGNIN}>Sign in</Link>
</> </>
)} )}
{props.mode === "signin" && ( {mode === "signin" && (
<> <>
<Link to="/signup">Create an account</Link> <Link to={ROUTES.SIGNUP}>Create an account</Link>
<Link to="/forgotpass">Forgot password</Link> <Link to={ROUTES.FORGOT_PASS}>Forgot password</Link>
</> </>
)} )}
</div> </div>
@ -160,6 +160,6 @@ function Auth(props) {
</form> </form>
</div> </div>
); );
} };
export default Auth; export default Auth;

View file

@ -1,13 +0,0 @@
import React from "react";
function Avatar(props) {
const { image, size, alt, ...otherProps } = props;
return (
<figure className={"image" + (size ? ` is-${size}x${size}` : "")}>
<img className="is-rounded" src={image} alt={alt} {...otherProps} />
</figure>
);
}
export default Avatar;

View file

@ -0,0 +1,9 @@
import React from "react";
const Avatar = ({ image, size, alt, ...otherProps }) => (
<figure className={"image" + (size ? ` is-${size}x${size}` : "")}>
<img className="is-rounded" src={image} alt={alt} {...otherProps} />
</figure>
);
export default Avatar;

View file

@ -1,16 +0,0 @@
import React from "react";
import "./BackgroundImage.scss";
function BackgroundImage(props) {
return (
<div
className="BackgroundImage"
style={{
"--image": `url(${props.image})`,
"--opacity": props.opacity
}}
/>
);
}
export default BackgroundImage;

View file

@ -0,0 +1,14 @@
import React from "react";
import "./BackgroundImage.scss";
const BackgroundImage = ({ opacity, image }) => (
<div
className="BackgroundImage"
style={{
"--image": `url(${image})`,
"--opacity": opacity,
}}
/>
);
export default BackgroundImage;

View file

@ -1,11 +0,0 @@
import React from "react";
function CenteredColumns(props) {
return (
<div className="columns is-centered is-variable is-4 is-multiline">
{props.children}
</div>
);
}
export default CenteredColumns;

View file

@ -0,0 +1,9 @@
import React from "react";
const CenteredColumns = ({ children }) => (
<div className="columns is-centered is-variable is-4 is-multiline">
{children}
</div>
);
export default CenteredColumns;

View file

@ -2,7 +2,7 @@ import React, { useState } from "react";
import Auth from "./Auth"; import Auth from "./Auth";
import { useAuth } from "./../util/auth.js"; import { useAuth } from "./../util/auth.js";
function ChangePass(props) { const ChangePass = ({ parentColor, buttonText }) => {
const auth = useAuth(); const auth = useAuth();
const [status, setStatus] = useState(); const [status, setStatus] = useState();
@ -13,13 +13,13 @@ function ChangePass(props) {
.then(() => { .then(() => {
setStatus({ setStatus({
type: "success", type: "success",
message: "Your password has been changed" message: "Your password has been changed",
}); });
}) })
.catch(error => { .catch((error) => {
setStatus({ setStatus({
type: "error", type: "error",
message: error.message message: error.message,
}); });
}); });
}; };
@ -27,12 +27,12 @@ function ChangePass(props) {
return ( return (
<Auth <Auth
mode="changepass" mode="changepass"
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.parentColor} parentColor={parentColor}
onSubmit={onSubmit} onSubmit={onSubmit}
status={status} status={status}
/> />
); );
} };
export default ChangePass; export default ChangePass;

View file

@ -1,22 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import ChangePass from "./ChangePass";
function ChangePassSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<ChangePass buttonText={props.buttonText} parentColor={props.color} />
</div>
</Section>
);
}
export default ChangePassSection;

View file

@ -0,0 +1,20 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import ChangePass from "./ChangePass";
const ChangePassSection = ({ color, size, title, subtitle, buttonText }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<ChangePass buttonText={buttonText} parentColor={color} />
</div>
</Section>
);
export default ChangePassSection;

View file

@ -1,18 +0,0 @@
import React from "react";
import "./Clients.scss";
function Clients(props) {
return (
<div className="columns is-centered is-multiline">
{props.items.map((item, index) => (
<div className="column is-narrow has-text-centered" key={index}>
<div className="Clients__logo">
<img src={item.image} width={item.width} alt={item.name} />
</div>
</div>
))}
</div>
);
}
export default Clients;

View file

@ -0,0 +1,16 @@
import React from "react";
import "./Clients.scss";
const Clients = ({ items }) => (
<div className="columns is-centered is-multiline">
{items.map((item, index) => (
<div className="column is-narrow has-text-centered" key={index}>
<div className="Clients__logo">
<img src={item.image} width={item.width} alt={item.name} />
</div>
</div>
))}
</div>
);
export default Clients;

View file

@ -1,45 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Clients from "./Clients";
function ClientsSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Clients
items={[
{
name: "Instagram",
image: "https://uploads.divjoy.com/logo-instagram.svg",
width: "150px"
},
{
name: "Slack",
image: "https://uploads.divjoy.com/logo-slack.svg",
width: "135px"
},
{
name: "Tinder",
image: "https://uploads.divjoy.com/logo-tinder.svg",
width: "90px"
},
{
name: "Spotify",
image: "https://uploads.divjoy.com/logo-spotify.svg",
width: "135px"
}
]}
/>
</div>
</Section>
);
}
export default ClientsSection;

View file

@ -0,0 +1,43 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Clients from "./Clients";
const ClientsSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Clients
items={[
{
name: "Instagram",
image: "https://uploads.divjoy.com/logo-instagram.svg",
width: "150px",
},
{
name: "Slack",
image: "https://uploads.divjoy.com/logo-slack.svg",
width: "135px",
},
{
name: "Tinder",
image: "https://uploads.divjoy.com/logo-tinder.svg",
width: "90px",
},
{
name: "Spotify",
image: "https://uploads.divjoy.com/logo-spotify.svg",
width: "135px",
},
]}
/>
</div>
</Section>
);
export default ClientsSection;

View file

@ -2,7 +2,7 @@ import React, { useState } from "react";
import ContactForm from "./ContactForm"; import ContactForm from "./ContactForm";
import contact from "./../util/contact.js"; import contact from "./../util/contact.js";
function Contact(props) { const Contact = ({ parentColor, showNameField, buttonText }) => {
const [status, setStatus] = useState(); const [status, setStatus] = useState();
const onSubmit = ({ name, email, message }) => { const onSubmit = ({ name, email, message }) => {
@ -11,19 +11,19 @@ function Contact(props) {
contact.submit({ name, email, message }).then(() => { contact.submit({ name, email, message }).then(() => {
setStatus({ setStatus({
type: "success", type: "success",
message: "Your message has been sent! We'll get back to you soon." message: "Your message has been sent! We'll get back to you soon.",
}); });
}); });
}; };
return ( return (
<ContactForm <ContactForm
parentColor={props.parentColor} parentColor={parentColor}
showNameField={props.showNameField} showNameField={showNameField}
buttonText={props.buttonText} buttonText={buttonText}
onSubmit={onSubmit} onSubmit={onSubmit}
status={status} status={status}
/> />
); );
} };
export default Contact; export default Contact;

View file

@ -3,7 +3,13 @@ import FormStatus from "./FormStatus";
import FormField from "./FormField"; import FormField from "./FormField";
import SectionButton from "./SectionButton"; import SectionButton from "./SectionButton";
function ContactForm(props) { const ContactForm = ({
showNameField,
onSubmit,
status,
parentColor,
buttonText,
}) => {
// State for input values // State for input values
const [name, setName] = useState(""); const [name, setName] = useState("");
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@ -19,18 +25,18 @@ function ContactForm(props) {
let errors = []; let errors = [];
// Function for fetching error for a field // Function for fetching error for a field
const getError = field => { const getError = (field) => {
return errors.find(e => e.field === field); return errors.find((e) => e.field === field);
}; };
// Function to see if field is empty // Function to see if field is empty
const isEmpty = val => val.trim() === ""; const isEmpty = (val) => val.trim() === "";
// Add error if email empty // Add error if email empty
if (isEmpty(email)) { if (isEmpty(email)) {
errors.push({ errors.push({
field: "email", field: "email",
message: "Please enter an email" message: "Please enter an email",
}); });
} }
@ -38,32 +44,32 @@ function ContactForm(props) {
if (isEmpty(message)) { if (isEmpty(message)) {
errors.push({ errors.push({
field: "message", field: "message",
message: "Please enter a message" message: "Please enter a message",
}); });
} }
// Add error if name shown and empty // Add error if name shown and empty
if (props.showNameField) { if (showNameField) {
if (isEmpty(name)) { if (isEmpty(name)) {
errors.push({ errors.push({
field: "nom", field: "nom",
message: "Please enter your name" message: "Please enter your name",
}); });
} }
} }
// Handle form submission // Handle form submission
const handleSubmit = e => { const handleSubmit = (e) => {
// If field errors then show them // If field errors then show them
if (errors.length) { if (errors.length) {
setShowErrors(true); setShowErrors(true);
} else { } else {
// Otherwise call onSubmit with form data // Otherwise call onSubmit with form data
if (props.onSubmit) { if (onSubmit) {
props.onSubmit({ onSubmit({
name, name,
email, email,
message message,
}); });
} }
} }
@ -71,25 +77,25 @@ function ContactForm(props) {
return ( return (
<> <>
{props.status && props.status.message && ( {status && status.message && (
<FormStatus type={props.status.type} message={props.status.message} /> <FormStatus type={status.type} message={status.message} />
)} )}
<form <form
onSubmit={e => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
handleSubmit(); handleSubmit();
}} }}
> >
<div className="field is-horizontal"> <div className="field is-horizontal">
<div className="field-body"> <div className="field-body">
{props.showNameField && ( {showNameField && (
<FormField <FormField
value={name} value={name}
type="text" type="text"
placeholder="Nom" placeholder="Nom"
error={showErrors && getError("name")} error={showErrors && getError("name")}
onChange={value => setName(value)} onChange={(value) => setName(value)}
/> />
)} )}
@ -98,7 +104,7 @@ function ContactForm(props) {
type="email" type="email"
placeholder="Email" placeholder="Email"
error={showErrors && getError("email")} error={showErrors && getError("email")}
onChange={value => setEmail(value)} onChange={(value) => setEmail(value)}
/> />
</div> </div>
</div> </div>
@ -109,7 +115,7 @@ function ContactForm(props) {
type="textarea" type="textarea"
placeholder="Message" placeholder="Message"
error={showErrors && getError("message")} error={showErrors && getError("message")}
onChange={value => setMessage(value)} onChange={(value) => setMessage(value)}
/> />
</div> </div>
</div> </div>
@ -118,15 +124,13 @@ function ContactForm(props) {
<div className="field"> <div className="field">
<div className="control"> <div className="control">
<SectionButton <SectionButton
parentColor={props.parentColor} parentColor={parentColor}
size="medium" size="medium"
state={ state={
props.status && props.status.type === "pending" status && status.type === "pending" ? "loading" : "normal"
? "loading"
: "normal"
} }
> >
{props.buttonText} {buttonText}
</SectionButton> </SectionButton>
</div> </div>
</div> </div>
@ -135,6 +139,6 @@ function ContactForm(props) {
</form> </form>
</> </>
); );
} };
export default ContactForm; export default ContactForm;

View file

@ -1,27 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Contact from "./Contact";
import "./ContactSection.scss";
function ContactSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="ContactSection__container container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Contact
parentColor={props.color}
showNameField={props.showNameField}
buttonText={props.buttonText}
/>
</div>
</Section>
);
}
export default ContactSection;

View file

@ -0,0 +1,32 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Contact from "./Contact";
import "./ContactSection.scss";
const ContactSection = ({
color,
size,
title,
subtitle,
showNameField,
buttonText,
}) => (
<Section color={color} size={size}>
<div className="ContactSection__container container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Contact
parentColor={color}
showNameField={showNameField}
buttonText={buttonText}
/>
</div>
</Section>
);
export default ContactSection;

View file

@ -1,25 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
function ContentSection(props) {
return (
<Section
color={props.color}
size={props.size}
backgroundImage={props.backgroundImage}
backgroundImageOpacity={props.backgroundImageOpacity}
>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={2}
/>
</div>
</Section>
);
}
export default ContentSection;

View file

@ -0,0 +1,30 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
const ContentSection = ({
color,
size,
backgroundImage,
backgroundImageOpacity,
title,
subtitle,
}) => (
<Section
color={color}
size={size}
backgroundImage={backgroundImage}
backgroundImageOpacity={backgroundImageOpacity}
>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={2}
/>
</div>
</Section>
);
export default ContentSection;

View file

@ -1,148 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import {
Application,
ButtonGroup,
ButtonIcon,
ButtonMenu,
MenuDivider,
MenuItem,
Avatar,
Card,
Button,
Input,
} from 'react-rainbow-components';
// more details about how to use react-font-awesome
// visit https://github.com/FortAwesome/react-fontawesome
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faSearch,
faTasks,
faShareAlt,
faHeart,
faAngleDown,
faPencilAlt,
faBell,
} from '@fortawesome/free-solid-svg-icons';
const iconContainerStyles = {
width: '2rem',
height: '2rem',
};
const inputStyles = {
width: 260,
};
function DashboardSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
</div>
<section>
<header className="rainbow-align-content_space-between rainbow-background-color_white rainbow-p-vertical_medium react-rainbow-golbal-header">
<img
src="images/rainbow-logo.svg"
alt="rainbow logo"
className="rainbow-m-left_medium react-rainbow-global-header_logo"
/>
<article className="rainbow-flex rainbow-align_center">
<ButtonGroup>
<ButtonIcon
variant="border"
disabled
icon={<FontAwesomeIcon icon={faPencilAlt} />}
/>
<ButtonIcon
variant="border"
disabled
icon={<FontAwesomeIcon icon={faBell} />}
/>
<ButtonMenu
menuSize="x-small"
menuAlignment="right"
icon={<FontAwesomeIcon icon={faAngleDown} />}
>
<MenuItem label="Actions" variant="header" />
<MenuItem label="Terminer" />
<MenuItem label="Modifier" />
<MenuDivider variant="space" />
<MenuItem
label="Supprimer"
icon={<FontAwesomeIcon icon={faTasks} />}
iconPosition="right"
/>
</ButtonMenu>
</ButtonGroup>
<Avatar
src="images/user/user2.jpg"
variant="circle"
className="rainbow-m-horizontal_medium"
/>
</article>
</header>
<section className="rainbow-m-horizontal_large rainbow-m-top_large rainbow-m-bottom_xx-large">
<Card
title="Tâches"
icon={
<span
className="rainbow-background-color_brand rainbow-border-radius_circle rainbow-align-content_center"
style={iconContainerStyles}
>
<FontAwesomeIcon icon={faTasks} size="lg" className="rainbow-color_white" />
</span>
}
footer={
<div className="rainbow-align-content_space-between">
<div className="rainbow-flex">
<ButtonIcon
icon={<FontAwesomeIcon icon={faHeart} />}
className="rainbow-m-right_xx-small"
/>
<ButtonIcon icon={<FontAwesomeIcon icon={faShareAlt} />} />
</div>
<ButtonIcon icon={<FontAwesomeIcon icon={faAngleDown} />} />
</div>
}
actions={<Button variant="neutral" label="Nouvelle tâche" />}
>
<div className="rainbow-p-bottom_large">
<Input
label="aplication component search"
hideLabel
placeholder="Rechercher"
icon={<FontAwesomeIcon icon={faSearch} className="rainbow-color_gray-3" />}
type="search"
className="rainbow-p-around_small"
style={inputStyles}
/>
<div className="rainbow-p-horizontal_xx-large rainbow-flex_column rainbow-align-content_center">
<img
src="images/illustrations/Illustration-rainbow-1.svg"
className="rainbow-p-top_x-large rainbow-align_absolute-center"
alt="the rainbow"
/>
<p className="rainbow-p-top_medium rainbow-font-size-heading_small rainbow-color_gray-4">
Pas de nouvelles tâches
</p>
</div>
</div>
</Card>
</section>
</section>
</Section>
);
}
export default DashboardSection;

View file

@ -0,0 +1,154 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import {
ButtonGroup,
ButtonIcon,
ButtonMenu,
MenuDivider,
MenuItem,
Avatar,
Card,
Button,
Input,
} from "react-rainbow-components";
// more details about how to use react-font-awesome
// visit https://github.com/FortAwesome/react-fontawesome
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faSearch,
faTasks,
faShareAlt,
faHeart,
faAngleDown,
faPencilAlt,
faBell,
} from "@fortawesome/free-solid-svg-icons";
const iconContainerStyles = {
width: "2rem",
height: "2rem",
};
const inputStyles = {
width: 260,
};
const DashboardSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
</div>
<section>
<header className="rainbow-align-content_space-between rainbow-background-color_white rainbow-p-vertical_medium react-rainbow-golbal-header">
<img
src="images/rainbow-logo.svg"
alt="rainbow logo"
className="rainbow-m-left_medium react-rainbow-global-header_logo"
/>
<article className="rainbow-flex rainbow-align_center">
<ButtonGroup>
<ButtonIcon
variant="border"
disabled
icon={<FontAwesomeIcon icon={faPencilAlt} />}
/>
<ButtonIcon
variant="border"
disabled
icon={<FontAwesomeIcon icon={faBell} />}
/>
<ButtonMenu
menuSize="x-small"
menuAlignment="right"
icon={<FontAwesomeIcon icon={faAngleDown} />}
>
<MenuItem label="Actions" variant="header" />
<MenuItem label="Terminer" />
<MenuItem label="Modifier" />
<MenuDivider variant="space" />
<MenuItem
label="Supprimer"
icon={<FontAwesomeIcon icon={faTasks} />}
iconPosition="right"
/>
</ButtonMenu>
</ButtonGroup>
<Avatar
src="images/user/user2.jpg"
variant="circle"
className="rainbow-m-horizontal_medium"
/>
</article>
</header>
<section className="rainbow-m-horizontal_large rainbow-m-top_large rainbow-m-bottom_xx-large">
<Card
title="Tâches"
icon={
<span
className="rainbow-background-color_brand rainbow-border-radius_circle rainbow-align-content_center"
style={iconContainerStyles}
>
<FontAwesomeIcon
icon={faTasks}
size="lg"
className="rainbow-color_white"
/>
</span>
}
footer={
<div className="rainbow-align-content_space-between">
<div className="rainbow-flex">
<ButtonIcon
icon={<FontAwesomeIcon icon={faHeart} />}
className="rainbow-m-right_xx-small"
/>
<ButtonIcon icon={<FontAwesomeIcon icon={faShareAlt} />} />
</div>
<ButtonIcon icon={<FontAwesomeIcon icon={faAngleDown} />} />
</div>
}
actions={<Button variant="neutral" label="Nouvelle tâche" />}
>
<div className="rainbow-p-bottom_large">
<Input
label="aplication component search"
hideLabel
placeholder="Rechercher"
icon={
<FontAwesomeIcon
icon={faSearch}
className="rainbow-color_gray-3"
/>
}
type="search"
className="rainbow-p-around_small"
style={inputStyles}
/>
<div className="rainbow-p-horizontal_xx-large rainbow-flex_column rainbow-align-content_center">
<img
src="images/illustrations/Illustration-rainbow-1.svg"
className="rainbow-p-top_x-large rainbow-align_absolute-center"
alt="the rainbow"
/>
<p className="rainbow-p-top_medium rainbow-font-size-heading_small rainbow-color_gray-4">
Pas de nouvelles tâches
</p>
</div>
</div>
</Card>
</section>
</section>
</Section>
);
export default DashboardSection;

View file

@ -1,14 +0,0 @@
import React from "react";
import FaqItem from "./FaqItem";
function Faq(props) {
return (
<>
{props.items.map((item, index) => (
<FaqItem question={item.question} answer={item.answer} key={index} />
))}
</>
);
}
export default Faq;

12
src/components/Faq.jsx Normal file
View file

@ -0,0 +1,12 @@
import React from "react";
import FaqItem from "./FaqItem";
const Faq = ({ items }) => (
<>
{items.map((item, index) => (
<FaqItem question={item.question} answer={item.answer} key={index} />
))}
</>
);
export default Faq;

View file

@ -1,7 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import "./FaqItem.scss"; import "./FaqItem.scss";
function FaqItem(props) { const FaqItem = ({ question, answer }) => {
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
return ( return (
@ -16,12 +16,12 @@ function FaqItem(props) {
} }
/> />
</span> </span>
{props.question} {question}
</div> </div>
{expanded && <div className="subtitle">{props.answer}</div>} {expanded && <div className="subtitle">{answer}</div>}
</article> </article>
); );
} };
export default FaqItem; export default FaqItem;

View file

@ -1,50 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Faq from "./Faq";
function FaqSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Faq
items={[
{
question: "Integer ornare neque mauris?",
answer:
"Integer ornare neque mauris, ac vulputate lacus venenatis et. Pellentesque ut ultrices purus. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo."
},
{
question: "Lorem ipsum dolor sit amet?",
answer:
"Nunc nulla mauris, laoreet vel cursus lacinia, consectetur sit amet tellus. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo."
},
{
question: "Suspendisse ut tincidunt?",
answer:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lobortis, metus et mattis ullamcorper. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo."
},
{
question: "Ut enim ad minim veniam?",
answer:
"Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo."
},
{
question: "In velit mi, rhoncus dictum neque?",
answer:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud."
}
]}
/>
</div>
</Section>
);
}
export default FaqSection;

View file

@ -0,0 +1,48 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Faq from "./Faq";
const FaqSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Faq
items={[
{
question: "Integer ornare neque mauris?",
answer:
"Integer ornare neque mauris, ac vulputate lacus venenatis et. Pellentesque ut ultrices purus. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo.",
},
{
question: "Lorem ipsum dolor sit amet?",
answer:
"Nunc nulla mauris, laoreet vel cursus lacinia, consectetur sit amet tellus. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo.",
},
{
question: "Suspendisse ut tincidunt?",
answer:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In lobortis, metus et mattis ullamcorper. Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo.",
},
{
question: "Ut enim ad minim veniam?",
answer:
"Suspendisse ut tincidunt eros. In velit mi, rhoncus dictum neque a, tincidunt lobortis justo.",
},
{
question: "In velit mi, rhoncus dictum neque?",
answer:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud.",
},
]}
/>
</div>
</Section>
);
export default FaqSection;

View file

@ -1,29 +0,0 @@
import React from "react";
import "./Features.scss";
function Features(props) {
return (
<div className="Features">
{props.items.map((item, index) => (
<div
className="Features__columns columns is-variable is-8 is-vcentered has-text-centered-mobile"
key={index}
>
<div className="column is-half">
<h3 className="Features__title title has-text-weight-bold is-spaced is-3">
{item.title}
</h3>
<p className="subtitle">{item.description}</p>
</div>
<div className="column">
<figure className="Features__image image">
<img src={item.image} alt={item.title} />
</figure>
</div>
</div>
))}
</div>
);
}
export default Features;

View file

@ -0,0 +1,27 @@
import React from "react";
import "./Features.scss";
const Features = ({ items }) => (
<div className="Features">
{items.map((item, index) => (
<div
className="Features__columns columns is-variable is-8 is-vcentered has-text-centered-mobile"
key={index}
>
<div className="column is-half">
<h3 className="Features__title title has-text-weight-bold is-spaced is-3">
{item.title}
</h3>
<p className="subtitle">{item.description}</p>
</div>
<div className="column">
<figure className="Features__image image">
<img src={item.image} alt={item.title} />
</figure>
</div>
</div>
))}
</div>
);
export default Features;

View file

@ -1,50 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Features from "./Features";
function FeaturesSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Features
items={[
{
title: "Automatisez des tâches →",
description:
"Avis déchéances, suivi des encaissements, comptabilité locative, quittancement, …",
image: "https://uploads.divjoy.com/undraw-mind_map_cwng.svg"
},
{
title: "Protégez vos intérêts →",
description:
"Gestion des retards de paiement, révision du loyer, régularisation des charges, …",
image:
"https://uploads.divjoy.com/undraw-personal_settings_kihd.svg"
},
{
title: "Soyez mieux accompagné →",
description:
"Edition du bail, assistance administrative et comptable, information juridique et fiscale, …",
image: "https://uploads.divjoy.com/undraw-having_fun_iais.svg"
},
{
title: "Restez organisé (à venir) →",
description:
"Assignation des dépenses, archivage des justificatifs, déclaration des revenus fonciers, …",
image: "https://uploads.divjoy.com/undraw-balloons_vxx5.svg"
}
]}
/>
</div>
</Section>
);
}
export default FeaturesSection;

View file

@ -0,0 +1,48 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Features from "./Features";
const FeaturesSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Features
items={[
{
title: "Automatisez des tâches →",
description:
"Avis déchéances, suivi des encaissements, comptabilité locative, quittancement, …",
image: "https://uploads.divjoy.com/undraw-mind_map_cwng.svg",
},
{
title: "Protégez vos intérêts →",
description:
"Gestion des retards de paiement, révision du loyer, régularisation des charges, …",
image:
"https://uploads.divjoy.com/undraw-personal_settings_kihd.svg",
},
{
title: "Soyez mieux accompagné →",
description:
"Edition du bail, assistance administrative et comptable, information juridique et fiscale, …",
image: "https://uploads.divjoy.com/undraw-having_fun_iais.svg",
},
{
title: "Restez organisé (à venir) →",
description:
"Assignation des dépenses, archivage des justificatifs, déclaration des revenus fonciers, …",
image: "https://uploads.divjoy.com/undraw-balloons_vxx5.svg",
},
]}
/>
</div>
</Section>
);
export default FeaturesSection;

View file

@ -1,73 +0,0 @@
import React from "react";
import Section from "./Section";
import { Link } from "./../util/router.js";
import "./Footer.scss";
function Footer(props) {
return (
<Section color={props.color} size={props.size}>
<div className="FooterComponent__container container">
<div className="brand left">
<Link to="/">
<img src={props.logo} alt="Logo" />
</Link>
</div>
<div className="links right">
<Link to="/about">À propos</Link>
<Link to="/faq">FAQ</Link>
<Link to="/contact">Contact</Link>
</div>
<div className="social right">
<a
href="https://twitter.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-twitter" />
</span>
</a>
<a
href="https://facebook.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-facebook-f" />
</span>
</a>
<a
href="https://instagram.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-instagram" />
</span>
</a>
<a
href="https://linkedin.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-linkedin" />
</span>
</a>
<a
href="https://medium.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-medium" />
</span>
</a>
</div>
<div className="copyright left">{props.copyright}</div>
</div>
</Section>
);
}
export default Footer;

67
src/components/Footer.jsx Normal file
View file

@ -0,0 +1,67 @@
import React from "react";
import Section from "./Section";
import { Link } from "./../util/router.js";
import * as ROUTES from "../global/routes";
import "./Footer.scss";
const Footer = ({ color, size, logo, copyright }) => (
<Section color={color} size={size}>
<div className="FooterComponent__container container">
<div className="brand left">
<Link to={ROUTES.HOME}>
<img src={logo} alt="Logo" />
</Link>
</div>
<div className="links right">
<Link to={ROUTES.ABOUT}>À propos</Link>
<Link to={ROUTES.FAQ}>FAQ</Link>
<Link to={ROUTES.CONTACT}>Contact</Link>
</div>
<div className="social right">
<a href="https://twitter.com" target="_blank" rel="noopener noreferrer">
<span className="icon">
<i className="fab fa-twitter" />
</span>
</a>
<a
href="https://facebook.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-facebook-f" />
</span>
</a>
<a
href="https://instagram.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-instagram" />
</span>
</a>
<a
href="https://linkedin.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-linkedin" />
</span>
</a>
<a href="https://medium.com" target="_blank" rel="noopener noreferrer">
<span className="icon">
<i className="fab fa-medium" />
</span>
</a>
</div>
<div className="copyright left">{copyright}</div>
</div>
</Section>
);
export default Footer;

View file

@ -2,7 +2,7 @@ import React, { useState } from "react";
import Auth from "./Auth"; import Auth from "./Auth";
import { useAuth } from "./../util/auth.js"; import { useAuth } from "./../util/auth.js";
function ForgotPass(props) { const ForgotPass = ({ buttonText, parentColor }) => {
const auth = useAuth(); const auth = useAuth();
const [status, setStatus] = useState(); const [status, setStatus] = useState();
@ -13,13 +13,13 @@ function ForgotPass(props) {
.then(() => { .then(() => {
setStatus({ setStatus({
type: "success", type: "success",
message: "Password reset email sent" message: "Password reset email sent",
}); });
}) })
.catch(error => { .catch((error) => {
setStatus({ setStatus({
type: "error", type: "error",
message: error.message message: error.message,
}); });
}); });
}; };
@ -27,12 +27,12 @@ function ForgotPass(props) {
return ( return (
<Auth <Auth
mode="forgotpass" mode="forgotpass"
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.parentColor} parentColor={parentColor}
onSubmit={onSubmit} onSubmit={onSubmit}
status={status} status={status}
/> />
); );
} };
export default ForgotPass; export default ForgotPass;

View file

@ -1,22 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import ForgotPass from "./ForgotPass";
function ForgotPassSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<ForgotPass buttonText={props.buttonText} parentColor={props.color} />
</div>
</Section>
);
}
export default ForgotPassSection;

View file

@ -0,0 +1,21 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import ForgotPass from "./ForgotPass";
const ForgotPassSection = ({ color, size, title, subtitle, buttonText }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<ForgotPass buttonText={buttonText} parentColor={color} />
</div>
</Section>
);
export default ForgotPassSection;

View file

@ -1,33 +0,0 @@
import React from "react";
function FormField(props) {
return (
<div className="field">
<div className="control">
{props.type === "textarea" && (
<textarea
className="textarea is-medium"
type={props.type}
value={props.value}
placeholder={props.placeholder}
onChange={e => props.onChange(e.target.value)}
/>
)}
{props.type !== "textarea" && (
<input
className="input is-medium"
type={props.type}
value={props.value}
placeholder={props.placeholder}
onChange={e => props.onChange(e.target.value)}
/>
)}
</div>
{props.error && <p className="help is-danger">{props.error.message}</p>}
</div>
);
}
export default FormField;

View file

@ -0,0 +1,31 @@
import React from "react";
const FormField = ({ type, value, placeholder, onChange, error }) => (
<div className="field">
<div className="control">
{type === "textarea" && (
<textarea
className="textarea is-medium"
type={type}
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
/>
)}
{type !== "textarea" && (
<input
className="input is-medium"
type={type}
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
/>
)}
</div>
{error && <p className="help is-danger">{error.message}</p>}
</div>
);
export default FormField;

View file

@ -1,17 +0,0 @@
import React from "react";
function FormStatus(props) {
return (
<div
className={
"notification" +
(props.type === "error" ? " is-danger" : "") +
(props.type === "success" ? " is-success" : "")
}
>
{props.message}
</div>
);
}
export default FormStatus;

View file

@ -0,0 +1,15 @@
import React from "react";
const FormStatus = ({ type, message }) => (
<div
className={
"notification" +
(type === "error" ? " is-danger" : "") +
(type === "success" ? " is-success" : "")
}
>
{message}
</div>
);
export default FormStatus;

View file

@ -1,38 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import SectionButton from "./SectionButton";
import "./HeroSection.scss";
function HeroSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<div className="columns is-vcentered is-desktop">
<div className="column is-5-desktop has-text-centered-touch">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
size={1}
/>
<SectionButton
parentColor={props.color}
size="medium"
onClick={props.buttonOnClick}
>
{props.buttonText}
</SectionButton>
</div>
<div className="column is-1" />
<div className="column">
<figure className="HeroSection__image image">
<img src={props.image} alt="Illustration" />
</figure>
</div>
</div>
</div>
</Section>
);
}
export default HeroSection;

View file

@ -0,0 +1,40 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import SectionButton from "./SectionButton";
import "./HeroSection.scss";
const HeroSection = ({
color,
size,
title,
subtitle,
buttonOnClick,
buttonText,
image,
}) => (
<Section color={color} size={size}>
<div className="container">
<div className="columns is-vcentered is-desktop">
<div className="column is-5-desktop has-text-centered-touch">
<SectionHeader title={title} subtitle={subtitle} size={1} />
<SectionButton
parentColor={color}
size="medium"
onClick={buttonOnClick}
>
{buttonText}
</SectionButton>
</div>
<div className="column is-1" />
<div className="column">
<figure className="HeroSection__image image">
<img src={image} alt="Illustration" />
</figure>
</div>
</div>
</div>
</Section>
);
export default HeroSection;

View file

@ -1,19 +1,22 @@
import React, { useState } from "react"; import React, { useState } from "react";
import NavbarContainer from "./NavbarContainer"; import NavbarContainer from "./NavbarContainer";
import { Link } from "./../util/router.js"; import { Link } from "./../util/router.js";
import { useAuth } from "./../util/auth.js"; import { useAuth } from "./../util/auth.js";
import * as ROUTES from "../global/routes";
function Navbar(props) { const Navbar = ({ spaced, color, logo }) => {
const auth = useAuth(); const auth = useAuth();
const [menuOpen, setMenuOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false);
return ( return (
<NavbarContainer spaced={props.spaced} color={props.color}> <NavbarContainer spaced={spaced} color={color}>
<div className="container"> <div className="container">
<div className="navbar-brand"> <div className="navbar-brand">
<div className="navbar-item"> <div className="navbar-item">
<Link to="/"> <Link to={ROUTES.HOME}>
<img className="image" src={props.logo} alt="Logo" /> <img className="image" src={logo} alt="Logo" />
</Link> </Link>
</div> </div>
<div <div
@ -29,17 +32,17 @@ function Navbar(props) {
<div className="navbar-end"> <div className="navbar-end">
{auth.user && ( {auth.user && (
<div className="navbar-item has-dropdown is-hoverable"> <div className="navbar-item has-dropdown is-hoverable">
<Link className="navbar-link" to="/"> <Link className="navbar-link" to={ROUTES.HOME}>
Mon compte Mon compte
</Link> </Link>
<div className="navbar-dropdown is-boxed"> <div className="navbar-dropdown is-boxed">
<Link className="navbar-item" to="/dashboard"> <Link className="navbar-item" to={ROUTES.DASHBOARD}>
Dashboard Dashboard
</Link> </Link>
<Link <Link
className="navbar-item" className="navbar-item"
to="/signout" to={ROUTES.SIGNOUT}
onClick={e => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
auth.signout(); auth.signout();
}} }}
@ -51,7 +54,7 @@ function Navbar(props) {
)} )}
{!auth.user && ( {!auth.user && (
<Link className="navbar-item" to="/signin"> <Link className="navbar-item" to={ROUTES.SIGNIN}>
Connexion Connexion
</Link> </Link>
)} )}
@ -60,6 +63,6 @@ function Navbar(props) {
</div> </div>
</NavbarContainer> </NavbarContainer>
); );
} };
export default Navbar; export default Navbar;

View file

@ -1,17 +0,0 @@
import React from "react";
function NavbarContainer(props) {
return (
<nav
className={
"navbar" +
(props.color ? ` is-${props.color}` : "") +
(props.spaced ? " is-spaced" : "")
}
>
{props.children}
</nav>
);
}
export default NavbarContainer;

View file

@ -0,0 +1,13 @@
import React from "react";
const NavbarContainer = ({ color, spaced, children }) => (
<nav
className={
"navbar" + (color ? ` is-${color}` : "") + (spaced ? " is-spaced" : "")
}
>
{children}
</nav>
);
export default NavbarContainer;

View file

@ -2,16 +2,23 @@ import React, { useState } from "react";
import SectionButton from "./SectionButton"; import SectionButton from "./SectionButton";
import newsletter from "./../util/newsletter.js"; import newsletter from "./../util/newsletter.js";
function Newsletter(props) { const Newsletter = ({
onSubscribed,
size,
inputPlaceholder,
parentColor,
buttonOnClick,
buttonText,
subscribedMessage,
}) => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [subscribed, setSubscribed] = useState(false); const [subscribed, setSubscribed] = useState(false);
const handleSubmit = () => { const handleSubmit = () => {
if (email) { if (email) {
setSubscribed(true); setSubscribed(true);
// Parent component can optionally // Parent component can optionally, find out when subscribed.
// find out when subscribed. onSubscribed && onSubscribed();
props.onSubscribed && props.onSubscribed();
// Subscribe them // Subscribe them
newsletter.subscribe({ email }); newsletter.subscribe({ email });
} }
@ -21,7 +28,7 @@ function Newsletter(props) {
<> <>
{subscribed === false && ( {subscribed === false && (
<form <form
onSubmit={e => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
handleSubmit(); handleSubmit();
}} }}
@ -29,28 +36,28 @@ function Newsletter(props) {
<div className="field is-grouped"> <div className="field is-grouped">
<div className="control is-expanded"> <div className="control is-expanded">
<input <input
className={`input is-${props.size}`} className={`input is-${size}`}
type="email" type="email"
placeholder={props.inputPlaceholder} placeholder={inputPlaceholder}
onChange={event => setEmail(event.target.value)} onChange={(event) => setEmail(event.target.value)}
/> />
</div> </div>
<div className="control"> <div className="control">
<SectionButton <SectionButton
parentColor={props.parentColor} parentColor={parentColor}
size={props.size} size={size}
onClick={props.buttonOnClick} onClick={buttonOnClick}
> >
{props.buttonText} {buttonText}
</SectionButton> </SectionButton>
</div> </div>
</div> </div>
</form> </form>
)} )}
{subscribed === true && <>{props.subscribedMessage}</>} {subscribed === true && <>{subscribedMessage}</>}
</> </>
); );
} };
export default Newsletter; export default Newsletter;

View file

@ -1,33 +0,0 @@
import React from "react";
import Section from "./Section";
import Newsletter from "./Newsletter";
function NewsletterSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<div className="columns is-centered">
<div className="column is-12 is-10-fullhd">
<div className="columns is-vcentered">
<div className="column is-half">
<div className="title">{props.title}</div>
<div className="subtitle">{props.subtitle}</div>
</div>
<div className="column is-half">
<Newsletter
parentColor={props.color}
buttonText={props.buttonText}
inputPlaceholder={props.inputPlaceholder}
subscribedMessage={props.subscribedMessage}
size="medium"
/>
</div>
</div>
</div>
</div>
</div>
</Section>
);
}
export default NewsletterSection;

View file

@ -0,0 +1,39 @@
import React from "react";
import Section from "./Section";
import Newsletter from "./Newsletter";
const NewsletterSection = ({
color,
size,
title,
subtitle,
buttonText,
inputPlaceholder,
subscribedMessage,
}) => (
<Section color={color} size={size}>
<div className="container">
<div className="columns is-centered">
<div className="column is-12 is-10-fullhd">
<div className="columns is-vcentered">
<div className="column is-half">
<div className="title">{title}</div>
<div className="subtitle">{subtitle}</div>
</div>
<div className="column is-half">
<Newsletter
parentColor={color}
buttonText={buttonText}
inputPlaceholder={inputPlaceholder}
subscribedMessage={subscribedMessage}
size="medium"
/>
</div>
</div>
</div>
</div>
</div>
</Section>
);
export default NewsletterSection;

View file

@ -1,39 +0,0 @@
import React from "react";
import "./Pricing.scss";
function Pricing(props) {
return (
<div className="columns is-centered is-variable is-5">
{props.items.map((item, index) => (
<div className="Pricing__column column" key={index}>
<div
className={
"Pricing__card card" +
(item.emphasized === true ? " emphasized" : "")
}
>
<div className="Pricing__card-content card-content">
<div className="Pricing__period has-text-weight-bold">
{item.timespan}
</div>
<div className="Pricing__price has-text-weight-bold">
<span className="Pricing__price-symbol is-size-3">$</span>
<span className="is-size-1">{item.price}</span>
<span className="Pricing__price-month is-size-4">/m</span>
</div>
<p className="Pricing__description">{item.description}</p>
<button
className="Pricing__button button is-medium is-primary"
onClick={() => props.onChoosePlan(item.id)}
>
{props.buttonText}
</button>
</div>
</div>
</div>
))}
</div>
);
}
export default Pricing;

View file

@ -0,0 +1,37 @@
import React from "react";
import "./Pricing.scss";
const Pricing = ({ items, onChoosePlan, buttonText }) => (
<div className="columns is-centered is-variable is-5">
{items.map((item, index) => (
<div className="Pricing__column column" key={index}>
<div
className={
"Pricing__card card" +
(item.emphasized === true ? " emphasized" : "")
}
>
<div className="Pricing__card-content card-content">
<div className="Pricing__period has-text-weight-bold">
{item.timespan}
</div>
<div className="Pricing__price has-text-weight-bold">
<span className="Pricing__price-symbol is-size-3">$</span>
<span className="is-size-1">{item.price}</span>
<span className="Pricing__price-month is-size-4">/m</span>
</div>
<p className="Pricing__description">{item.description}</p>
<button
className="Pricing__button button is-medium is-primary"
onClick={() => onChoosePlan(item.id)}
>
{buttonText}
</button>
</div>
</div>
</div>
))}
</div>
);
export default Pricing;

View file

@ -1,45 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Pricing from "./Pricing";
import "./PricingSection.scss";
function PricingSection(props) {
return (
<Section color={props.color} size={props.size} id="pricing">
<div className="PricingSection__container container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Pricing
buttonText="Choose"
onChoosePlan={planId => {
// Add your own payments logic here
alert(`You chose the plan "${planId}"`);
}}
items={[
{
id: "monthly",
timespan: "Monthly",
price: "29",
description:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam."
},
{
id: "yearly",
timespan: "Yearly",
price: "19",
description:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae."
}
]}
/>
</div>
</Section>
);
}
export default PricingSection;

View file

@ -0,0 +1,43 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Pricing from "./Pricing";
import "./PricingSection.scss";
const PricingSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size} id="pricing">
<div className="PricingSection__container container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Pricing
buttonText="Choose"
onChoosePlan={(planId) => {
// Add your own payments logic here
alert(`You chose the plan "${planId}"`);
}}
items={[
{
id: "monthly",
timespan: "Monthly",
price: "29",
description:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam.",
},
{
id: "yearly",
timespan: "Yearly",
price: "19",
description:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae.",
},
]}
/>
</div>
</Section>
);
export default PricingSection;

View file

@ -1,37 +0,0 @@
import React from "react";
import BackgroundImage from "./BackgroundImage";
import "./Section.scss";
function Section(props) {
const {
color,
size,
backgroundImage,
backgroundImageOpacity,
children,
// Passed to section element
...otherProps
} = props;
return (
<section
className={
"SectionComponent hero section is-block is-relative" +
(color ? ` is-${color}` : "") +
(size ? ` is-${size}` : "")
}
{...otherProps}
>
{backgroundImage && (
<BackgroundImage
image={backgroundImage}
opacity={backgroundImageOpacity}
/>
)}
{props.children}
</section>
);
}
export default Section;

View file

@ -0,0 +1,33 @@
import React from "react";
import BackgroundImage from "./BackgroundImage";
import "./Section.scss";
const Section = ({
color,
size,
backgroundImage,
backgroundImageOpacity,
children,
// Passed to section element
...otherProps
}) => (
<section
className={
"SectionComponent hero section is-block is-relative" +
(color ? ` is-${color}` : "") +
(size ? ` is-${size}` : "")
}
{...otherProps}
>
{backgroundImage && (
<BackgroundImage
image={backgroundImage}
opacity={backgroundImageOpacity}
/>
)}
{children}
</section>
);
export default Section;

View file

@ -1,42 +0,0 @@
import React from "react";
function SectionButton(props) {
const {
parentColor,
size,
state,
fullWidth,
// Passed to button element
...otherProps
} = props;
return (
<button
className={
"button" +
([
"primary",
"info",
"success",
"warning",
"danger",
"black",
"dark"
].includes(parentColor)
? ` is-${parentColor} is-inverted`
: "") +
(["white", "light"].includes(parentColor) || !parentColor
? " is-primary"
: "") +
(size ? ` is-${size}` : "") +
(state ? ` is-${state}` : "") +
(fullWidth ? " is-fullwidth" : "")
}
{...otherProps}
>
{props.children}
</button>
);
}
export default SectionButton;

View file

@ -0,0 +1,39 @@
import React from "react";
const SectionButton = ({
children,
parentColor,
size,
state,
fullWidth,
// Passed to button element
...otherProps
}) => (
<button
className={
"button" +
([
"primary",
"info",
"success",
"warning",
"danger",
"black",
"dark",
].includes(parentColor)
? ` is-${parentColor} is-inverted`
: "") +
(["white", "light"].includes(parentColor) || !parentColor
? " is-primary"
: "") +
(size ? ` is-${size}` : "") +
(state ? ` is-${state}` : "") +
(fullWidth ? " is-fullwidth" : "")
}
{...otherProps}
>
{children}
</button>
);
export default SectionButton;

View file

@ -1,36 +0,0 @@
import React from "react";
import "./SectionHeader.scss";
function SectionHeader(props) {
return (
<>
{(props.title || props.subtitle) && (
<header
className={
"SectionHeader__header" + (props.centered ? " is-centered" : "")
}
>
{props.title && (
<h1
className={
"title is-spaced has-text-weight-bold" +
(props.size ? ` is-${props.size}` : "") +
(props.size === 1 ? " is-size-2-mobile" : "")
}
>
{props.title}
</h1>
)}
{props.subtitle && (
<p className={"subtitle" + (props.size > 4 ? " is-6" : "")}>
{props.subtitle}
</p>
)}
</header>
)}
</>
);
}
export default SectionHeader;

View file

@ -0,0 +1,30 @@
import React from "react";
import "./SectionHeader.scss";
const SectionHeader = ({ title, subtitle, centered, size }) => (
<>
{(title || subtitle) && (
<header
className={"SectionHeader__header" + (centered ? " is-centered" : "")}
>
{title && (
<h1
className={
"title is-spaced has-text-weight-bold" +
(size ? ` is-${size}` : "") +
(size === 1 ? " is-size-2-mobile" : "")
}
>
{title}
</h1>
)}
{subtitle && (
<p className={"subtitle" + (size > 4 ? " is-6" : "")}>{subtitle}</p>
)}
</header>
)}
</>
);
export default SectionHeader;

View file

@ -2,7 +2,7 @@ import React, { useState } from "react";
import Auth from "./Auth"; import Auth from "./Auth";
import { useAuth } from "./../util/auth.js"; import { useAuth } from "./../util/auth.js";
function SignIn(props) { const SignIn = ({ onSignin, buttonText, parentColor }) => {
const auth = useAuth(); const auth = useAuth();
const [status, setStatus] = useState(); const [status, setStatus] = useState();
@ -10,13 +10,13 @@ function SignIn(props) {
setStatus({ type: "pending" }); setStatus({ type: "pending" });
auth auth
.signin(email, pass) .signin(email, pass)
.then(user => { .then((user) => {
props.onSignin && props.onSignin(); onSignin && onSignin();
}) })
.catch(error => { .catch((error) => {
setStatus({ setStatus({
type: "error", type: "error",
message: error.message message: error.message,
}); });
}); });
}; };
@ -24,12 +24,12 @@ function SignIn(props) {
return ( return (
<Auth <Auth
mode="signin" mode="signin"
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.parentColor} parentColor={parentColor}
onSubmit={onSubmit} onSubmit={onSubmit}
status={status} status={status}
/> />
); );
} };
export default SignIn; export default SignIn;

View file

@ -1,34 +1,37 @@
import React from "react"; import React from "react";
import Section from "./Section"; import Section from "./Section";
import SectionHeader from "./SectionHeader"; import SectionHeader from "./SectionHeader";
import SignIn from "./SignIn"; import SignIn from "./SignIn";
import { useRouter } from "./../util/router.js";
function SignInSection(props) { import { useRouter } from "./../util/router.js";
import * as ROUTES from "../global/routes";
const SignInSection = ({ color, size, title, subtitle, buttonText }) => {
const router = useRouter(); const router = useRouter();
// Go to page after signin // Go to page after signin
const onSignin = () => { const onSignin = () => {
router.push("/dashboard"); router.push(ROUTES.DASHBOARD);
}; };
return ( return (
<Section color={props.color} size={props.size}> <Section color={color} size={size}>
<div className="container"> <div className="container">
<SectionHeader <SectionHeader
title={props.title} title={title}
subtitle={props.subtitle} subtitle={subtitle}
centered={true} centered={true}
size={3} size={3}
/> />
<SignIn <SignIn
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.color} parentColor={color}
onSignin={onSignin} onSignin={onSignin}
/> />
</div> </div>
</Section> </Section>
); );
} };
export default SignInSection; export default SignInSection;

View file

@ -2,7 +2,7 @@ import React, { useState } from "react";
import Auth from "./Auth"; import Auth from "./Auth";
import { useAuth } from "./../util/auth.js"; import { useAuth } from "./../util/auth.js";
function SignUp(props) { const SignUp = ({ onSignup, buttonText, parentColor }) => {
const auth = useAuth(); const auth = useAuth();
const [status, setStatus] = useState(); const [status, setStatus] = useState();
@ -10,13 +10,13 @@ function SignUp(props) {
setStatus({ type: "pending" }); setStatus({ type: "pending" });
auth auth
.signup(email, pass) .signup(email, pass)
.then(user => { .then((user) => {
props.onSignup && props.onSignup(); onSignup && onSignup();
}) })
.catch(error => { .catch((error) => {
setStatus({ setStatus({
type: "error", type: "error",
message: error.message message: error.message,
}); });
}); });
}; };
@ -24,12 +24,12 @@ function SignUp(props) {
return ( return (
<Auth <Auth
mode="signup" mode="signup"
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.parentColor} parentColor={parentColor}
onSubmit={onSubmit} onSubmit={onSubmit}
status={status} status={status}
/> />
); );
} };
export default SignUp; export default SignUp;

View file

@ -1,34 +1,37 @@
import React from "react"; import React from "react";
import Section from "./Section"; import Section from "./Section";
import SectionHeader from "./SectionHeader"; import SectionHeader from "./SectionHeader";
import SignUp from "./SignUp"; import SignUp from "./SignUp";
import { useRouter } from "./../util/router.js";
function SignUpSection(props) { import { useRouter } from "./../util/router.js";
import * as ROUTES from "../global/routes";
const SignUpSection = ({ color, size, title, subtitle, buttonText }) => {
const router = useRouter(); const router = useRouter();
// Go to page after signup // Go to page after signup
const onSignup = () => { const onSignup = () => {
router.push("/dashboard"); router.push(ROUTES.DASHBOARD);
}; };
return ( return (
<Section color={props.color} size={props.size}> <Section color={color} size={size}>
<div className="container"> <div className="container">
<SectionHeader <SectionHeader
title={props.title} title={title}
subtitle={props.subtitle} subtitle={subtitle}
centered={true} centered={true}
size={3} size={3}
/> />
<SignUp <SignUp
buttonText={props.buttonText} buttonText={buttonText}
parentColor={props.color} parentColor={color}
onSignup={onSignup} onSignup={onSignup}
/> />
</div> </div>
</Section> </Section>
); );
} };
export default SignUpSection; export default SignUpSection;

View file

@ -1,34 +0,0 @@
import React from "react";
import CenteredColumns from "./CenteredColumns";
import Avatar from "./Avatar";
import "./TeamBios.scss";
function TeamBios(props) {
return (
<CenteredColumns>
{props.people.map((person, index) => (
<div
className="column is-half-tablet is-one-third-desktop is-flex"
key={index}
>
<div className="TeamBios__card card is-flex">
<div className="TeamBios__card-content card-content is-flex has-text-centered">
<div className="TeamBios__avatar-wrapper">
<Avatar image={person.avatar} size={128} alt={person.name} />
</div>
<div className="TeamBios__details">
<p className="is-size-5">{person.name}</p>
<p className="is-size-7 is-uppercase has-text-weight-semibold">
{person.role}
</p>
<p className="TeamBios__bio">{person.bio}</p>
</div>
</div>
</div>
</div>
))}
</CenteredColumns>
);
}
export default TeamBios;

View file

@ -0,0 +1,32 @@
import React from "react";
import CenteredColumns from "./CenteredColumns";
import Avatar from "./Avatar";
import "./TeamBios.scss";
const TeamBios = ({ people }) => (
<CenteredColumns>
{people.map((person, index) => (
<div
className="column is-half-tablet is-one-third-desktop is-flex"
key={index}
>
<div className="TeamBios__card card is-flex">
<div className="TeamBios__card-content card-content is-flex has-text-centered">
<div className="TeamBios__avatar-wrapper">
<Avatar image={person.avatar} size={128} alt={person.name} />
</div>
<div className="TeamBios__details">
<p className="is-size-5">{person.name}</p>
<p className="is-size-7 is-uppercase has-text-weight-semibold">
{person.role}
</p>
<p className="TeamBios__bio">{person.bio}</p>
</div>
</div>
</div>
</div>
))}
</CenteredColumns>
);
export default TeamBios;

View file

@ -1,55 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import TeamBios from "./TeamBios";
function TeamBiosSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<TeamBios
people={[
{
avatar: "https://uploads.divjoy.com/pravatar-150x-68.jpeg",
name: "John Smith",
role: "Software Engineer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo.",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com"
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-35.jpeg",
name: "Lisa Zinn",
role: "Software Engineer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo, corporis totam! Labore reprehenderit beatae magnam animi!",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com"
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-16.jpeg",
name: "Diana Low",
role: "Designer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo, corporis totam! Labore reprehenderit beatae magnam animi!",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com"
}
]}
/>
</div>
</Section>
);
}
export default TeamBiosSection;

View file

@ -0,0 +1,53 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import TeamBios from "./TeamBios";
const TeamBiosSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<TeamBios
people={[
{
avatar: "https://uploads.divjoy.com/pravatar-150x-68.jpeg",
name: "John Smith",
role: "Software Engineer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo.",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com",
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-35.jpeg",
name: "Lisa Zinn",
role: "Software Engineer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo, corporis totam! Labore reprehenderit beatae magnam animi!",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com",
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-16.jpeg",
name: "Diana Low",
role: "Designer",
bio:
"Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum consequatur numquam aliquam tenetur ad amet inventore hic beatae, quas accusantium perferendis sapiente explicabo, corporis totam! Labore reprehenderit beatae magnam animi!",
twitterUrl: "https://twitter.com",
facebookUrl: "https://facebook.com",
linkedinUrl: "https://linkedin.com",
},
]}
/>
</div>
</Section>
);
export default TeamBiosSection;

View file

@ -1,31 +0,0 @@
import React from "react";
import CenteredColumns from "./CenteredColumns";
import Avatar from "./Avatar";
import "./Testimonials.scss";
function Testimonials(props) {
return (
<CenteredColumns>
{props.items.map((item, index) => (
<div className="column is-flex" key={index}>
<div className="Testimonials__card card is-flex">
<div className="Testimonials__card-content card-content has-text-centered is-flex">
<div className="Testimonials__avatar-wrapper">
<Avatar image={item.avatar} size={96} alt={item.name} />
</div>
<p className="Testimonials__quote">"{item.bio}"</p>
<div className="Testimonials__info">
<div className="has-text-weight-bold">{item.name}</div>
<div className="Testimonials__company link is-size-7">
{item.company}
</div>
</div>
</div>
</div>
</div>
))}
</CenteredColumns>
);
}
export default Testimonials;

View file

@ -0,0 +1,29 @@
import React from "react";
import CenteredColumns from "./CenteredColumns";
import Avatar from "./Avatar";
import "./Testimonials.scss";
const Testimonials = ({ items }) => (
<CenteredColumns>
{items.map((item, index) => (
<div className="column is-flex" key={index}>
<div className="Testimonials__card card is-flex">
<div className="Testimonials__card-content card-content has-text-centered is-flex">
<div className="Testimonials__avatar-wrapper">
<Avatar image={item.avatar} size={96} alt={item.name} />
</div>
<p className="Testimonials__quote">"{item.bio}"</p>
<div className="Testimonials__info">
<div className="has-text-weight-bold">{item.name}</div>
<div className="Testimonials__company link is-size-7">
{item.company}
</div>
</div>
</div>
</div>
</div>
))}
</CenteredColumns>
);
export default Testimonials;

View file

@ -1,48 +0,0 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Testimonials from "./Testimonials";
function TestimonialsSection(props) {
return (
<Section color={props.color} size={props.size}>
<div className="container">
<SectionHeader
title={props.title}
subtitle={props.subtitle}
centered={true}
size={3}
/>
<Testimonials
items={[
{
avatar: "https://uploads.divjoy.com/pravatar-150x-5.jpeg",
name: "Sarah Kline",
bio:
"Ce que jaime dans cet outil, cest surtout sa simplicité dutilisation ! Enfin quelque chose de bien pensé, rassurant et moderne [...]",
company: "Company"
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-48.jpeg",
name: "Shawna Murray",
role: "Software Engineer",
bio:
"Jai 4 locations à gérer : jétais vite débordé avant, mais maintenant tout est centralisé sur mon compte. Si je n'ai pas le temps de me connecter, je reçois quand même toujours les notifications par mail !",
company: "Company"
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-12.jpeg",
name: "Blake Elder",
role: "Designer",
bio:
"Avant, jenvoyais un chèque par la poste... mais cest bien plus pratique maintenant de payer mon loyer : réglé en un clic, trace du paiement, quittance envoyée par email...",
company: "Company"
}
]}
/>
</div>
</Section>
);
}
export default TestimonialsSection;

View file

@ -0,0 +1,46 @@
import React from "react";
import Section from "./Section";
import SectionHeader from "./SectionHeader";
import Testimonials from "./Testimonials";
const TestimonialsSection = ({ color, size, title, subtitle }) => (
<Section color={color} size={size}>
<div className="container">
<SectionHeader
title={title}
subtitle={subtitle}
centered={true}
size={3}
/>
<Testimonials
items={[
{
avatar: "https://uploads.divjoy.com/pravatar-150x-5.jpeg",
name: "Sarah Kline",
bio:
"Ce que jaime dans cet outil, cest surtout sa simplicité dutilisation ! Enfin quelque chose de bien pensé, rassurant et moderne [...]",
company: "Company",
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-48.jpeg",
name: "Shawna Murray",
role: "Software Engineer",
bio:
"Jai 4 locations à gérer : jétais vite débordé avant, mais maintenant tout est centralisé sur mon compte. Si je n'ai pas le temps de me connecter, je reçois quand même toujours les notifications par mail !",
company: "Company",
},
{
avatar: "https://uploads.divjoy.com/pravatar-150x-12.jpeg",
name: "Blake Elder",
role: "Designer",
bio:
"Avant, jenvoyais un chèque par la poste... mais cest bien plus pratique maintenant de payer mon loyer : réglé en un clic, trace du paiement, quittance envoyée par email...",
company: "Company",
},
]}
/>
</div>
</Section>
);
export default TestimonialsSection;

11
src/global/routes.js Normal file
View file

@ -0,0 +1,11 @@
export const HOME = "/";
export const ABOUT = "/about";
export const FAQ = "/faq";
export const CONTACT = "/contact";
export const DASHBOARD = "/dashboard";
export const SIGNIN = "/signin";
export const SIGNUP = "/signup";
export const SIGNOUT = "/signout";
export const FORGOT_PASS = "/forgotpass";
export const CHANGE_PASS = "/changepass";
export const PRICING = "/pricing";

View file

@ -6,7 +6,4 @@ import * as serviceWorker from "./serviceWorker";
ReactDom.render(<App />, document.getElementById("root")); ReactDom.render(<App />, document.getElementById("root"));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister(); serviceWorker.unregister();

View file

@ -1,80 +0,0 @@
import React from "react";
import Navbar from "./../components/Navbar";
import IndexPage from "./index";
import AboutPage from "./about";
import FaqPage from "./faq";
import PricingPage from "./pricing";
import ContactPage from "./contact";
import DashboardPage from "./dashboard";
import SigninPage from "./signin";
import SignupPage from "./signup";
import ForgotpassPage from "./forgotpass";
import ChangepassPage from "./changepass";
import { Switch, Route, Router } from "./../util/router.js";
import Footer from "./../components/Footer";
import "./../util/analytics.js";
import { ProvideAuth } from "./../util/auth.js";
function App(props) {
return (
<ProvideAuth>
<Router>
<>
<Navbar
color="white"
spaced={true}
logo="https://uploads.divjoy.com/logo.svg"
/>
<Switch>
<Route exact path="/" component={IndexPage} />
<Route exact path="/about" component={AboutPage} />
<Route exact path="/faq" component={FaqPage} />
<Route exact path="/pricing" component={PricingPage} />
<Route exact path="/contact" component={ContactPage} />
<Route exact path="/dashboard" component={DashboardPage} />
<Route exact path="/signin" component={SigninPage} />
<Route exact path="/signup" component={SignupPage} />
<Route exact path="/forgotpass" component={ForgotpassPage} />
<Route exact path="/changepass" component={ChangepassPage} />
<Route
component={({ location }) => {
return (
<div
style={{
padding: "50px",
width: "100%",
textAlign: "center"
}}
>
The page <code>{location.pathname}</code> could not be
found.
</div>
);
}}
/>
</Switch>
<Footer
color="light"
size="normal"
logo="https://uploads.divjoy.com/logo.svg"
copyright="© 2019 nemo.immo"
/>
</>
</Router>
</ProvideAuth>
);
}
export default App;

71
src/pages/_app.jsx Normal file
View file

@ -0,0 +1,71 @@
import React from "react";
import IndexPage from "./index";
import AboutPage from "./about";
import FaqPage from "./faq";
import PricingPage from "./pricing";
import ContactPage from "./contact";
import DashboardPage from "./dashboard";
import SigninPage from "./signin";
import SignupPage from "./signup";
import ForgotpassPage from "./forgotpass";
import ChangepassPage from "./changepass";
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import { Switch, Route, Router } from "../util/router.js";
import { ProvideAuth } from "../util/auth.js";
import * as ROUTES from "../global/routes";
import "../util/analytics.js";
const App = () => (
<ProvideAuth>
<Router>
<>
<Navbar
color="white"
spaced={true}
logo="https://uploads.divjoy.com/logo.svg"
/>
<Switch>
<Route exact path={ROUTES.HOME} component={IndexPage} />
<Route exact path={ROUTES.ABOUT} component={AboutPage} />
<Route exact path={ROUTES.FAQ} component={FaqPage} />
<Route exact path={ROUTES.PRICING} component={PricingPage} />
<Route exact path={ROUTES.CONTACT} component={ContactPage} />
<Route exact path={ROUTES.DASHBOARD} component={DashboardPage} />
<Route exact path={ROUTES.SIGNIN} component={SigninPage} />
<Route exact path={ROUTES.SIGNUP} component={SignupPage} />
<Route exact path={ROUTES.FORGOT_PASS} component={ForgotpassPage} />
<Route exact path={ROUTES.CHANGE_PASS} component={ChangepassPage} />
<Route
component={({ location }) => {
return (
<div
style={{
padding: "50px",
width: "100%",
textAlign: "center",
}}
>
The page <code>{location.pathname}</code> could not be found.
</div>
);
}}
/>
</Switch>
<Footer
color="light"
size="normal"
logo="https://uploads.divjoy.com/logo.svg"
copyright="© 2019 nemo.immo"
/>
</>
</Router>
</ProvideAuth>
);
export default App;

View file

@ -1,24 +0,0 @@
import React from "react";
import ContentSection from "./../components/ContentSection";
import TeamBiosSection from "./../components/TeamBiosSection";
function AboutPage(props) {
return (
<>
<ContentSection
color="primary"
size="large"
title="Nous vous faisons gagner du temps"
subtitle="Plus de soucis grâce à Ruidy & TiNyny"
/>
<TeamBiosSection
color="white"
size="medium"
title="L'équipe à votre service"
subtitle=""
/>
</>
);
}
export default AboutPage;

23
src/pages/about.jsx Normal file
View file

@ -0,0 +1,23 @@
import React from "react";
import ContentSection from "../components/ContentSection";
import TeamBiosSection from "../components/TeamBiosSection";
const AboutPage = () => (
<>
<ContentSection
color="primary"
size="large"
title="Nous vous faisons gagner du temps"
subtitle="Plus de soucis grâce à Ruidy & TiNyny"
/>
<TeamBiosSection
color="white"
size="medium"
title="L'équipe à votre service"
subtitle=""
/>
</>
);
export default AboutPage;

View file

@ -1,16 +0,0 @@
import React from "react";
import ChangePassSection from "./../components/ChangePassSection";
function ChangepassPage(props) {
return (
<ChangePassSection
color="white"
size="large"
title="Choose a new password"
subtitle=""
buttonText="Change password"
/>
);
}
export default ChangepassPage;

15
src/pages/changepass.jsx Normal file
View file

@ -0,0 +1,15 @@
import React from "react";
import ChangePassSection from "../components/ChangePassSection";
const ChangepassPage = () => (
<ChangePassSection
color="white"
size="large"
title="Choose a new password"
subtitle=""
buttonText="Change password"
/>
);
export default ChangepassPage;

View file

@ -1,17 +0,0 @@
import React from "react";
import ContactSection from "./../components/ContactSection";
function ContactPage(props) {
return (
<ContactSection
color="white"
size="medium"
title="Contactez-nous"
subtitle="C'est un jeu d'enfant"
showNameField={true}
buttonText="Envoyer"
/>
);
}
export default ContactPage;

16
src/pages/contact.jsx Normal file
View file

@ -0,0 +1,16 @@
import React from "react";
import ContactSection from "./../components/ContactSection";
const ContactPage = () => (
<ContactSection
color="white"
size="medium"
title="Contactez-nous"
subtitle="C'est un jeu d'enfant"
showNameField={true}
buttonText="Envoyer"
/>
);
export default ContactPage;

View file

@ -1,28 +0,0 @@
import React, { useEffect } from "react";
import DashboardSection from "./../components/DashboardSection";
import { useAuth } from "./../util/auth.js";
import { useRouter } from "./../util/router.js";
function DashboardPage(props) {
const auth = useAuth();
const router = useRouter();
// Redirect to signin
// if not signed in.
useEffect(() => {
if (auth.user === false) {
router.push("/signin");
}
}, [auth, router]);
return (
<DashboardSection
color="white"
size="large"
// title="Dashboard"
// subtitle="Dashboard components are coming to the Divjoy library soon. For now, you can implement a custom dashboard here after exporting your code."
/>
);
}
export default DashboardPage;

30
src/pages/dashboard.jsx Normal file
View file

@ -0,0 +1,30 @@
import React, { useEffect } from "react";
import DashboardSection from "../components/DashboardSection";
import { useAuth } from "../util/auth.js";
import { useRouter } from "../util/router.js";
import * as ROUTES from "../global/routes";
const DashboardPage = () => {
const auth = useAuth();
const router = useRouter();
// Redirect to signin if not signed in.
useEffect(() => {
if (auth.user === false) {
router.push(ROUTES.SIGNIN);
}
}, [auth, router]);
return (
<DashboardSection
color="white"
size="large"
title="Dashboard"
subtitle="Dashboard components are coming to the Divjoy library soon. For now, you can implement a custom dashboard here after exporting your code."
/>
);
};
export default DashboardPage;

View file

@ -1,15 +0,0 @@
import React from "react";
import FaqSection from "./../components/FaqSection";
function FaqPage(props) {
return (
<FaqSection
color="white"
size="medium"
title="Foire Aux Questions"
subtitle=""
/>
);
}
export default FaqPage;

14
src/pages/faq.jsx Normal file
View file

@ -0,0 +1,14 @@
import React from "react";
import FaqSection from "../components/FaqSection";
const FaqPage = () => (
<FaqSection
color="white"
size="medium"
title="Foire Aux Questions"
subtitle=""
/>
);
export default FaqPage;

View file

@ -1,16 +0,0 @@
import React from "react";
import ForgotPassSection from "./../components/ForgotPassSection";
function ForgotpassPage(props) {
return (
<ForgotPassSection
color="white"
size="large"
title="Get a new password"
subtitle=""
buttonText="Reset password"
/>
);
}
export default ForgotpassPage;

15
src/pages/forgotpass.jsx Normal file
View file

@ -0,0 +1,15 @@
import React from "react";
import ForgotPassSection from "../components/ForgotPassSection";
const ForgotpassPage = () => (
<ForgotPassSection
color="white"
size="large"
title="Get a new password"
subtitle=""
buttonText="Reset password"
/>
);
export default ForgotpassPage;

View file

@ -1,12 +1,15 @@
import React from "react"; import React from "react";
import HeroSection from "./../components/HeroSection";
import ClientsSection from "./../components/ClientsSection";
import FeaturesSection from "./../components/FeaturesSection";
import TestimonialsSection from "./../components/TestimonialsSection";
import NewsletterSection from "./../components/NewsletterSection";
import { useRouter } from "./../util/router.js";
function IndexPage(props) { import HeroSection from "../components/HeroSection";
import ClientsSection from "../components/ClientsSection";
import FeaturesSection from "../components/FeaturesSection";
import TestimonialsSection from "../components/TestimonialsSection";
import NewsletterSection from "../components/NewsletterSection";
import { useRouter } from "../util/router.js";
import * as ROUTES from "../global/routes";
const IndexPage = () => {
const router = useRouter(); const router = useRouter();
return ( return (
@ -19,16 +22,11 @@ function IndexPage(props) {
buttonText="Commencer" buttonText="Commencer"
image="https://uploads.divjoy.com/undraw-japan_ubgk.svg" image="https://uploads.divjoy.com/undraw-japan_ubgk.svg"
buttonOnClick={() => { buttonOnClick={() => {
router.push("/pricing"); router.push(ROUTES.PRICING);
}} }}
/> />
<ClientsSection color="light" size="normal" title="" subtitle="" /> <ClientsSection color="light" size="normal" title="" subtitle="" />
<FeaturesSection <FeaturesSection color="white" size="medium" title="" subtitle="" />
color="white"
size="medium"
title=""
subtitle=""
/>
<TestimonialsSection <TestimonialsSection
color="light" color="light"
size="medium" size="medium"
@ -46,6 +44,6 @@ function IndexPage(props) {
/> />
</> </>
); );
} };
export default IndexPage; export default IndexPage;

View file

@ -1,10 +0,0 @@
import React from "react";
import PricingSection from "./../components/PricingSection";
function PricingPage(props) {
return (
<PricingSection color="white" size="medium" title="Pricing" subtitle="" />
);
}
export default PricingPage;

9
src/pages/pricing.jsx Normal file
View file

@ -0,0 +1,9 @@
import React from "react";
import PricingSection from "./../components/PricingSection";
const PricingPage = () => (
<PricingSection color="white" size="medium" title="Pricing" subtitle="" />
);
export default PricingPage;

View file

@ -1,16 +0,0 @@
import React from "react";
import SignInSection from "./../components/SignInSection";
function SigninPage(props) {
return (
<SignInSection
color="white"
size="large"
title="Welcome back"
subtitle=""
buttonText="Sign in"
/>
);
}
export default SigninPage;

15
src/pages/signin.jsx Normal file
View file

@ -0,0 +1,15 @@
import React from "react";
import SignInSection from "./../components/SignInSection";
const SigninPage = () => (
<SignInSection
color="white"
size="large"
title="Welcome back"
subtitle=""
buttonText="Sign in"
/>
);
export default SigninPage;

View file

@ -1,16 +0,0 @@
import React from "react";
import SignUpSection from "./../components/SignUpSection";
function SignupPage(props) {
return (
<SignUpSection
color="white"
size="large"
title="Get yourself an account"
subtitle=""
buttonText="Sign up"
/>
);
}
export default SignupPage;

15
src/pages/signup.jsx Normal file
View file

@ -0,0 +1,15 @@
import React from "react";
import SignUpSection from "./../components/SignUpSection";
const SignupPage = () => (
<SignUpSection
color="white"
size="large"
title="Get yourself an account"
subtitle=""
buttonText="Sign up"
/>
);
export default SignupPage;

View file

@ -7,11 +7,15 @@
// resources are updated in the background. // resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to // To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA // opt-in, read https://bit.ly/CRA-PWA
var isLocalhost = Boolean(window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. var isLocalhost = Boolean(
window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname === "localhost" || // [::1] is the IPv6 localhost address.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)); window.location.hostname === "[::1]" || // 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) { export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW. // The URL constructor is available in all browsers that support SW.
var publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); var publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
@ -22,7 +26,7 @@ export function register(config) {
return; return;
} }
window.addEventListener('load', function () { window.addEventListener("load", function () {
var swUrl = "".concat(process.env.PUBLIC_URL, "/service-worker.js"); var swUrl = "".concat(process.env.PUBLIC_URL, "/service-worker.js");
if (isLocalhost) { if (isLocalhost) {
@ -31,7 +35,9 @@ export function register(config) {
// service worker/PWA documentation. // service worker/PWA documentation.
navigator.serviceWorker.ready.then(function () { navigator.serviceWorker.ready.then(function () {
console.log('This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA'); console.log(
"This web app is being served cache-first by a service worker. To learn more, visit https://bit.ly/CRA-PWA"
);
}); });
} else { } else {
// Is not localhost. Just register service worker // Is not localhost. Just register service worker
@ -42,69 +48,82 @@ export function register(config) {
} }
function registerValidSW(swUrl, config) { function registerValidSW(swUrl, config) {
navigator.serviceWorker.register(swUrl).then(function (registration) { navigator.serviceWorker
registration.onupdatefound = function () { .register(swUrl)
var installingWorker = registration.installing; .then(function (registration) {
registration.onupdatefound = function () {
var installingWorker = registration.installing;
if (installingWorker == null) { if (installingWorker == null) {
return; return;
} }
installingWorker.onstatechange = function () { installingWorker.onstatechange = function () {
if (installingWorker.state === 'installed') { if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) { if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched, // At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older // but the previous service worker will still serve the older
// content until all client tabs are closed. // content until all client tabs are closed.
console.log('New content is available and will be used when all ' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'); // Execute callback console.log(
"New content is available and will be used when all " +
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
); // Execute callback
if (config && config.onUpdate) { if (config && config.onUpdate) {
config.onUpdate(registration); config.onUpdate(registration);
} }
} else { } else {
// At this point, everything has been precached. // At this point, everything has been precached.
// It's the perfect time to display a // It's the perfect time to display a
// "Content is cached for offline use." message. // "Content is cached for offline use." message.
console.log('Content is cached for offline use.'); // Execute callback console.log("Content is cached for offline use."); // Execute callback
if (config && config.onSuccess) { if (config && config.onSuccess) {
config.onSuccess(registration); config.onSuccess(registration);
}
} }
} }
} };
}; };
}; })
})["catch"](function (error) { ["catch"](function (error) {
console.error('Error during service worker registration:', error); console.error("Error during service worker registration:", error);
}); });
} }
function checkValidServiceWorker(swUrl, config) { function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page. // Check if the service worker can be found. If it can't reload the page.
fetch(swUrl).then(function (response) { fetch(swUrl)
// Ensure service worker exists, and that we really are getting a JS file. .then(function (response) {
var contentType = response.headers.get('content-type'); // Ensure service worker exists, and that we really are getting a JS file.
var contentType = response.headers.get("content-type");
if (response.status === 404 || contentType != null && contentType.indexOf('javascript') === -1) { if (
// No service worker found. Probably a different app. Reload the page. response.status === 404 ||
navigator.serviceWorker.ready.then(function (registration) { (contentType != null && contentType.indexOf("javascript") === -1)
registration.unregister().then(function () { ) {
window.location.reload(); // No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(function (registration) {
registration.unregister().then(function () {
window.location.reload();
});
}); });
}); } else {
} else { // Service worker found. Proceed as normal.
// Service worker found. Proceed as normal. registerValidSW(swUrl, config);
registerValidSW(swUrl, config); }
} })
})["catch"](function () { ["catch"](function () {
console.log('No internet connection found. App is running in offline mode.'); console.log(
}); "No internet connection found. App is running in offline mode."
);
});
} }
export function unregister() { export function unregister() {
if ('serviceWorker' in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready.then(function (registration) { navigator.serviceWorker.ready.then(function (registration) {
registration.unregister(); registration.unregister();
}); });
} }
} }

11997
yarn.lock Normal file

File diff suppressed because it is too large Load diff