This commit is contained in:
Ruidy Nemausat 2019-10-20 11:24:18 +02:00
parent 17d6218804
commit fee97cf1fd
129 changed files with 10080 additions and 682 deletions

View file

@ -6,23 +6,23 @@ In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br />
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br />
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br />
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

4551
divjoy-project.json Normal file

File diff suppressed because it is too large Load diff

3329
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,27 @@
{
"name": "sandbox",
"name": "my-divjoy-project",
"version": "0.1.0",
"private": true,
"keywords": [
"divjoy"
],
"dependencies": {
"react": "^16.10.2",
"react-dom": "^16.10.2",
"react-scripts": "3.2.0"
"@analytics/google-analytics": "0.2.0",
"analytics": "0.2.2",
"aws-sdk": "2.533.0",
"body-parser": "1.19.0",
"bulma": "0.7.5",
"css-loader": "^3.2.0",
"express": "4.17.1",
"firebase": "6.6.2",
"mailchimp-api-v3": "1.13.1",
"node-sass": "^4.12.0",
"query-string": "6.8.3",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-router-dom": "5.1.0",
"react-scripts": "3.1.2",
"resolve-url-loader": "^3.1.0"
},
"scripts": {
"start": "react-scripts start",
@ -27,5 +43,6 @@
"last 1 firefox version",
"last 1 safari version"
]
}
},
"proxy": "http://localhost:8080"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -2,19 +2,19 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- Font Awesome icons -->
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.10.1/css/all.css"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -6,20 +6,10 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
}

View file

@ -1,2 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

View file

@ -1,22 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #09d3ac;
}

View file

@ -1,26 +0,0 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;

View file

@ -1,9 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

View file

@ -0,0 +1,165 @@
import React, { useState } from "react";
import FormStatus from "./../FormStatus";
import FormField from "./../FormField";
import SectionButton from "./../SectionButton";
import { Link } from "./../../util/router.js";
import "./styles.scss";
function Auth(props) {
// State for all inputs
const [email, setEmail] = useState("");
const [pass, setPass] = useState("");
const [confirmPass, setConfirmPass] = useState("");
// Whether to show errors
// We set to true if they submit and there are errors.
// We only show errors after they submit because
// it's annoying to see errors while typing.
const [showErrors, setShowErrors] = useState(false);
// Error array we'll populate
let errors = [];
// Function for fetching error for a field
const getError = field => {
return errors.find(e => e.field === field);
};
// Function to see if field is empty
const isEmpty = val => val.trim() === "";
// Add error if email empty
if (["signin", "signup", "forgotpass"].includes(props.mode)) {
if (isEmpty(email)) {
errors.push({
field: "email",
message: "Please enter an email"
});
}
}
// Add error if password empty
if (["signin", "signup", "changepass"].includes(props.mode)) {
if (isEmpty(pass)) {
errors.push({
field: "pass",
message: "Please enter a password"
});
}
}
// Add error if confirmPass empty or
// if it doesn't match pass.
// Only for signup and changepass views.
if (["signup", "changepass"].includes(props.mode)) {
if (isEmpty(confirmPass)) {
errors.push({
field: "confirmPass",
message: "Please confirm password"
});
} else if (pass !== confirmPass) {
errors.push({
field: "confirmPass",
message: `This doesn't match your password`
});
}
}
// Handle form submission
const handleSubmit = () => {
// If field errors then show them
if (errors.length) {
setShowErrors(true);
} else {
// Otherwise call onSubmit with email/pass
if (props.onSubmit) {
props.onSubmit({
email,
pass
});
}
}
};
return (
<div className="Auth">
{props.status && props.status.message && (
<FormStatus type={props.status.type} message={props.status.message} />
)}
<form
onSubmit={e => {
e.preventDefault();
handleSubmit();
}}
>
{["signup", "signin", "forgotpass"].includes(props.mode) && (
<FormField
value={email}
type="email"
placeholder="Email"
error={showErrors && getError("email")}
onChange={value => setEmail(value)}
/>
)}
{["signup", "signin", "changepass"].includes(props.mode) && (
<FormField
value={pass}
type="password"
placeholder="Password"
error={showErrors && getError("pass")}
onChange={value => setPass(value)}
/>
)}
{["signup", "changepass"].includes(props.mode) && (
<FormField
value={confirmPass}
type="password"
placeholder="Confirm Password"
error={showErrors && getError("confirmPass")}
onChange={value => setConfirmPass(value)}
/>
)}
<div className="field">
<p className="control ">
<SectionButton
parentColor={props.parentColor}
size="medium"
fullWidth={true}
state={
props.status && props.status.type === "pending"
? "loading"
: "normal"
}
>
{props.buttonText}
</SectionButton>
</p>
</div>
{["signup", "signin"].includes(props.mode) && (
<div className="Auth__bottom-link has-text-centered">
{props.mode === "signup" && (
<>
Have an account already?
<Link to="/signin">Sign in</Link>
</>
)}
{props.mode === "signin" && (
<>
<Link to="/signup">Create an account</Link>
<Link to="/forgotpass">Forgot password</Link>
</>
)}
</div>
)}
</form>
</div>
);
}
export default Auth;

View file

@ -0,0 +1,13 @@
.Auth {
width: 100%;
max-width: 450px;
margin: 0 auto;
}
.Auth__bottom-link {
margin-top: 1rem;
font-size: 0.9rem;
a {
margin: 0 0.5rem;
}
}

View file

@ -0,0 +1,14 @@
import React from "react";
import "./styles.scss";
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

View file

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

View file

@ -0,0 +1,13 @@
.BackgroundImage {
content: "";
background-image: var(--image);
background-position: center center;
background-size: cover;
opacity: var(--opacity);
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
z-index: 0;
}

View file

@ -0,0 +1,12 @@
import React from "react";
import "./styles.scss";
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,39 @@
import React, { useState } from "react";
import Auth from "./../Auth";
import { useAuth } from "./../../util/auth.js";
import "./styles.scss";
function ChangePass(props) {
const auth = useAuth();
const [status, setStatus] = useState();
const onSubmit = ({ pass }) => {
setStatus({ type: "pending" });
auth
.confirmPasswordReset(pass)
.then(() => {
setStatus({
type: "success",
message: "Your password has been changed"
});
})
.catch(error => {
setStatus({
type: "error",
message: error.message
});
});
};
return (
<Auth
mode="changepass"
buttonText={props.buttonText}
parentColor={props.parentColor}
onSubmit={onSubmit}
status={status}
/>
);
}
export default ChangePass;

View file

View file

@ -0,0 +1,23 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import ChangePass from "./../ChangePass";
import "./styles.scss";
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,18 @@
import React from "react";
import "./styles.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,7 @@
.Clients__logo {
margin: 0 12px;
img {
// Removes extra space under image
vertical-align: bottom;
}
}

View file

@ -0,0 +1,46 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Clients from "./../Clients";
import "./styles.scss";
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,30 @@
import React, { useState } from "react";
import ContactForm from "./../ContactForm";
import contact from "./../../util/contact.js";
import "./styles.scss";
function Contact(props) {
const [status, setStatus] = useState();
const onSubmit = ({ name, email, message }) => {
setStatus({ type: "pending" });
contact.submit({ name, email, message }).then(() => {
setStatus({
type: "success",
message: "Your message has been sent! We'll get back to you soon."
});
});
};
return (
<ContactForm
parentColor={props.parentColor}
showNameField={props.showNameField}
buttonText={props.buttonText}
onSubmit={onSubmit}
status={status}
/>
);
}
export default Contact;

View file

View file

@ -0,0 +1,141 @@
import React, { useState } from "react";
import FormStatus from "./../FormStatus";
import FormField from "./../FormField";
import SectionButton from "./../SectionButton";
import "./styles.scss";
function ContactForm(props) {
// State for input values
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
// Whether to show errors
// We set to true if they submit and there are errors.
// We only show errors after they submit because
// it's annoying to see errors while typing.
const [showErrors, setShowErrors] = useState(false);
// Error array we'll populate
let errors = [];
// Function for fetching error for a field
const getError = field => {
return errors.find(e => e.field === field);
};
// Function to see if field is empty
const isEmpty = val => val.trim() === "";
// Add error if email empty
if (isEmpty(email)) {
errors.push({
field: "email",
message: "Please enter an email"
});
}
// Add error if message empty
if (isEmpty(message)) {
errors.push({
field: "message",
message: "Please enter a message"
});
}
// Add error if name shown and empty
if (props.showNameField) {
if (isEmpty(name)) {
errors.push({
field: "name",
message: "Please enter your name"
});
}
}
// Handle form submission
const handleSubmit = e => {
// If field errors then show them
if (errors.length) {
setShowErrors(true);
} else {
// Otherwise call onSubmit with form data
if (props.onSubmit) {
props.onSubmit({
name,
email,
message
});
}
}
};
return (
<>
{props.status && props.status.message && (
<FormStatus type={props.status.type} message={props.status.message} />
)}
<form
onSubmit={e => {
e.preventDefault();
handleSubmit();
}}
>
<div className="field is-horizontal">
<div className="field-body">
{props.showNameField && (
<FormField
value={name}
type="text"
placeholder="Name"
error={showErrors && getError("name")}
onChange={value => setName(value)}
/>
)}
<FormField
value={email}
type="email"
placeholder="Email"
error={showErrors && getError("email")}
onChange={value => setEmail(value)}
/>
</div>
</div>
<div className="field is-horizontal">
<div className="field-body">
<FormField
value={message}
type="textarea"
placeholder="Message"
error={showErrors && getError("message")}
onChange={value => setMessage(value)}
/>
</div>
</div>
<div className="field is-horizontal">
<div className="field-body">
<div className="field">
<div className="control">
<SectionButton
parentColor={props.parentColor}
size="medium"
state={
props.status && props.status.type === "pending"
? "loading"
: "normal"
}
>
{props.buttonText}
</SectionButton>
</div>
</div>
</div>
</div>
</form>
</>
);
}
export default ContactForm;

View file

View file

@ -0,0 +1,27 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Contact from "./../Contact";
import "./styles.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,3 @@
.ContactSection__container {
max-width: 850px;
}

View file

@ -0,0 +1,26 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import "./styles.scss";
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,21 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import "./styles.scss";
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>
);
}
export default DashboardSection;

View file

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

View file

View file

@ -0,0 +1,27 @@
import React, { useState } from "react";
import "./styles.scss";
function FaqItem(props) {
const [expanded, setExpanded] = useState(false);
return (
<article className="FaqItem" onClick={() => setExpanded(!expanded)}>
<div className="title is-spaced is-4">
<span className="FaqItem__icon icon is-size-5 has-text-primary">
<i
className={
"fas" +
(expanded ? " fa-minus" : "") +
(!expanded ? " fa-plus" : "")
}
/>
</span>
{props.question}
</div>
{expanded && <div className="subtitle">{props.answer}</div>}
</article>
);
}
export default FaqItem;

View file

@ -0,0 +1,12 @@
.FaqItem {
cursor: pointer;
padding: 1.6rem 0;
border-bottom: 1px solid #efefef;
&:last-child {
border-bottom: none;
}
}
.FaqItem__icon {
margin-right: 1rem;
}

View file

@ -0,0 +1,51 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Faq from "./../Faq";
import "./styles.scss";
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

View file

@ -0,0 +1,29 @@
import React from "react";
import "./styles.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 @@
.Features {
max-width: 900px;
margin: 80px auto 0 auto;
}
.Features__columns {
// Reverse every other row
&:nth-of-type(even) {
flex-direction: row-reverse;
}
&:not(:last-of-type) {
padding-bottom: 1.5rem;
@media screen and (min-width: 769px) {
padding-bottom: 2.5rem;
}
}
}
.Features__title {
margin-bottom: 1.2rem !important;
}
.Features__image {
max-width: 300px;
margin: 30px auto;
}

View file

@ -0,0 +1,51 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Features from "./../Features";
import "./styles.scss";
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,73 @@
import React from "react";
import Section from "./../Section";
import { Link } from "./../../util/router.js";
import "./styles.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://linkedin.com"
target="_blank"
rel="noopener noreferrer"
>
<span className="icon">
<i className="fab fa-linkedin" />
</span>
</a>
<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://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;

View file

@ -0,0 +1,57 @@
.FooterComponent__container {
display: flex;
flex-wrap: wrap;
> div {
display: flex;
flex: none;
justify-content: center;
width: 100%;
margin-bottom: 24px;
}
.brand {
img {
display: block;
height: 32px;
}
}
.social {
align-items: flex-end;
}
.social,
.links {
a {
color: inherit;
&:hover {
opacity: 0.8;
}
&:not(:first-of-type) {
margin-left: 20px;
}
}
}
// Tablet and up
@media screen and (min-width: 769px) {
> div {
flex: 50%;
}
.left {
justify-content: flex-start;
}
.right {
justify-content: flex-end;
}
// Move links to end (bottom right)
// Swaps position with social
.links {
order: 1;
}
}
}

View file

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

View file

View file

@ -0,0 +1,23 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import ForgotPass from "./../ForgotPass";
import "./styles.scss";
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,34 @@
import React from "react";
import "./styles.scss";
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

View file

@ -0,0 +1,18 @@
import React from "react";
import "./styles.scss";
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

View file

@ -0,0 +1,38 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import SectionButton from "./../SectionButton";
import "./styles.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,4 @@
.HeroSection__image {
margin: 0 auto;
max-width: 570px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -0,0 +1,66 @@
import React, { useState } from "react";
import NavbarContainer from "./../NavbarContainer";
import { Link } from "./../../util/router.js";
import { useAuth } from "./../../util/auth.js";
import "./styles.scss";
function Navbar(props) {
const auth = useAuth();
const [menuOpen, setMenuOpen] = useState(false);
return (
<NavbarContainer spaced={props.spaced} color={props.color}>
<div className="container">
<div className="navbar-brand">
<div className="navbar-item">
<Link to="/">
<img className="image" src={props.logo} alt="Logo" />
</Link>
</div>
<div
className={"navbar-burger burger" + (menuOpen ? " is-active" : "")}
onClick={() => setMenuOpen(!menuOpen)}
>
<span />
<span />
<span />
</div>
</div>
<div className={"navbar-menu" + (menuOpen ? " is-active" : "")}>
<div className="navbar-end">
{auth.user && (
<div className="navbar-item has-dropdown is-hoverable">
<Link className="navbar-link" to="/">
Account
</Link>
<div className="navbar-dropdown is-boxed">
<Link className="navbar-item" to="/dashboard">
Dashboard
</Link>
<Link
className="navbar-item"
to="/signout"
onClick={e => {
e.preventDefault();
auth.signout();
}}
>
Sign out
</Link>
</div>
</div>
)}
{!auth.user && (
<Link className="navbar-item" to="/signin">
Sign in
</Link>
)}
</div>
</div>
</div>
</NavbarContainer>
);
}
export default Navbar;

View file

View file

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

View file

View file

@ -0,0 +1,34 @@
import React from "react";
import Section from "./../Section";
import Newsletter from "./../Newsletter";
import "./styles.scss";
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 "./styles.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,46 @@
.Pricing__column {
display: flex;
align-items: stretch;
}
.Pricing__card {
display: flex;
// Stretch to fit column width
width: 100%;
// Ensure .card-content stretches to fit width
align-items: stretch;
}
.Pricing__card-content {
// Flex so that button can position self at
// bottom of card using margin-top auto.
display: flex;
flex-direction: column;
// Stretch to fit column width
width: 100%;
padding: 2rem;
}
.Pricing__period {
font-size: 1.25rem;
}
.Pricing__price {
margin: 20px 0;
}
.Pricing__price-symbol {
opacity: 0.4;
}
.Pricing__price-month {
opacity: 0.4;
}
.Pricing__description {
padding-bottom: 40px;
}
.Pricing__button {
margin-top: auto;
}

View file

@ -0,0 +1,45 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Pricing from "./../Pricing";
import "./styles.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,3 @@
.PricingSection__container {
max-width: 800px;
}

View file

@ -0,0 +1,37 @@
import React from "react";
import BackgroundImage from "./../BackgroundImage";
import "./styles.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,7 @@
.SectionComponent {
// Add light border if two white
// sections next to each other.
.is-white + &.is-white {
border-top: 1px solid #F0F0F0;
}
}

View file

@ -0,0 +1,43 @@
import React from "react";
import "./styles.scss";
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

View file

@ -0,0 +1,36 @@
import React from "react";
import "./styles.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,20 @@
.SectionHeader__header {
margin-bottom: 3rem;
// Remove margin if nothing after header
&:last-child {
margin-bottom: 0;
}
// Added if props.centered is true
&.is-centered {
text-align: center;
}
.subtitle {
max-width: 700px;
// So we can have max-width but still
// have alignment controlled by text-align.
display: inline-block;
}
}

View file

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

View file

View file

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

View file

View file

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

View file

View file

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

View file

View file

@ -0,0 +1,34 @@
import React from "react";
import CenteredColumns from "./../CenteredColumns";
import Avatar from "./../Avatar";
import "./styles.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,21 @@
.TeamBios__card {
flex-direction: column;
}
.TeamBios__card-content {
flex-direction: column;
height: 100%;
padding: 1.8rem;
}
.TeamBios__avatar-wrapper {
margin: 0 auto;
}
.TeamBios__details {
margin-top: 20px;
}
.TeamBios__bio {
margin-top: 20px;
}

View file

@ -0,0 +1,56 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import TeamBios from "./../TeamBios";
import "./styles.scss";
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,31 @@
import React from "react";
import CenteredColumns from "./../CenteredColumns";
import Avatar from "./../Avatar";
import "./styles.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,26 @@
.Testimonials__card {
flex-direction: column;
}
.Testimonials__card-content {
flex-direction: column;
height: 100%;
padding: 1.8rem;
}
.Testimonials__avatar-wrapper {
margin: 0 auto;
}
.Testimonials__quote {
margin-top: 30px;
}
.Testimonials__info {
margin-top: auto;
padding-top: 40px;
}
.Testimonials__company {
margin-top: 3px;
}

View file

@ -0,0 +1,49 @@
import React from "react";
import Section from "./../Section";
import SectionHeader from "./../SectionHeader";
import Testimonials from "./../Testimonials";
import "./styles.scss";
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

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

View file

@ -1,10 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import React from "react";
import ReactDom from "react-dom";
import "./styles.scss";
import App from "./pages/_app";
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.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8 KiB

81
src/pages/_app/index.js Normal file
View file

@ -0,0 +1,81 @@
import React from "react";
import Navbar from "./../../components/Navbar";
import HomePage from "./../home";
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 analytics from "./../../util/analytics.js";
import { ProvideAuth } from "./../../util/auth.js";
import "./styles.scss";
function App(props) {
return (
<ProvideAuth>
<Router>
<>
<Navbar
color="white"
spaced={true}
logo="https://uploads.divjoy.com/logo.svg"
/>
<Switch>
<Route exact path="/" component={HomePage} />
<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;

Some files were not shown because too many files have changed in this diff Show more