diff --git a/src/Application.js b/src/Application.js new file mode 100644 index 0000000..070ba51 --- /dev/null +++ b/src/Application.js @@ -0,0 +1,82 @@ +import React, {Fragment} from 'react'; +import PropTypes from 'prop-types'; +import isMobile from 'ismobilejs'; +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {Redirect, Route, Switch} from 'react-router-dom'; +import Header from './imports/components/Header'; +import ScrollToTop from './imports/components/ScrollToTop'; +import Intro from './pages/Intro'; +import MobileHome from './mobilePages/Journey/Home'; +import Home from './pages/Journey/Home'; +import MobileServices from './mobilePages/Journey/Services'; +import Services from './pages/Journey/Services'; +import MobileImpact from './mobilePages/Journey/Impact'; +import Impact from './pages/Journey/Impact'; +import MobileCaseStudies from './mobilePages/Journey/CaseStudies'; +import CaseStudies from './pages/Journey/CaseStudies'; +import MobileTeam from './mobilePages/Journey/Team'; +import Team from './pages/Journey/Team'; +import MobileConsultation from './mobilePages/Journey/Consultation'; +import Consultation from './pages/Journey/Consultation'; +import BetterYet from './pages/Projects/BetterYet'; +import LPMA from './pages/Projects/LPMA'; +import Hub from './pages/Projects/Hub'; +import {trackPage} from './imports/analytics'; + +export default class Application extends React.Component { + componentDidUpdate(prevProps) { + const {location: {prevPathname}} = prevProps; + const {location} = this.props; + const {pathname} = location; + if (prevPathname !== pathname) { + trackPage(pathname); + } + } + + render() { + const {location} = this.props; + return ( + + {(location.pathname !== '/' + && location.pathname !== '/betteryet' + && location.pathname !== '/lpma' + && location.pathname !== '/hub' + && ((isMobile.phone && location.pathname !== '/home') + || (!isMobile.phone)) + && ((isMobile.phone && location.pathname !== '/services') + || (!isMobile.phone)) + && ((isMobile.phone && location.pathname !== '/our-work') + || (!isMobile.phone)) + && ((isMobile.phone && location.pathname !== '/team') + || (!isMobile.phone)) + && ((isMobile.phone && location.pathname !== '/consult') + || (!isMobile.phone)) + && ((isMobile.phone && location.pathname !== '/impact') + || (!isMobile.phone))) &&
} + + + + + + + + + + + + + + + } /> + + + + + + ); + } +} + +Application.propTypes = { + location: PropTypes.object.isRequired, +}; diff --git a/src/app.js b/src/app.js index 2ff16d9..a1c7ec4 100644 --- a/src/app.js +++ b/src/app.js @@ -1,39 +1,17 @@ import React, {Component, Fragment} from 'react'; -import { - BrowserRouter, Route, Switch, Redirect, -} from 'react-router-dom'; -import {TransitionGroup, CSSTransition} from 'react-transition-group'; -import ScrollToTop from 'components/ScrollToTop'; +import {BrowserRouter, Route} from 'react-router-dom'; import DeviceOrientation, {Orientation} from 'components/Orientation'; import LoaderLine from 'components/Loader/LoaderLine'; -import Header from 'components/Header'; import isMobile from 'ismobilejs'; import 'normalize.css'; import 'styles/main.styl'; import AOS from 'aos'; import 'aos/dist/aos.css'; -import Intro from './pages/Intro'; -import Home from './pages/Journey/Home'; -import MobileHome from './mobilePages/Journey/Home'; -import Services from './pages/Journey/Services'; -import MobileServices from './mobilePages/Journey/Services'; -import Impact from './pages/Journey/Impact'; -import MobileImpact from './mobilePages/Journey/Impact'; -import CaseStudies from './pages/Journey/CaseStudies'; -import MobileCaseStudies from './mobilePages/Journey/CaseStudies'; -import Team from './pages/Journey/Team'; -import MobileTeam from './mobilePages/Journey/Team'; -import Consultation from './pages/Journey/Consultation'; -import MobileConsultation from './mobilePages/Journey/Consultation'; - -import BetterYet from './pages/Projects/BetterYet'; -import LPMA from './pages/Projects/LPMA'; -import Hub from './pages/Projects/Hub'; - import Landscape from './pages/Landscape'; import './index.styl'; +import Application from './Application'; AOS.init({ disable: false, @@ -89,53 +67,14 @@ class App extends Component { renderRoutes() { const {ready} = this.state; - - if (!ready) return null; + if (!ready) { + return false; + } return ( - ( - - {(location.pathname !== '/' - && location.pathname !== '/betteryet' - && location.pathname !== '/lpma' - && location.pathname !== '/hub' - && ((isMobile.phone && location.pathname !== '/home') - || (!isMobile.phone)) - && ((isMobile.phone && location.pathname !== '/services') - || (!isMobile.phone)) - && ((isMobile.phone && location.pathname !== '/our-work') - || (!isMobile.phone)) - && ((isMobile.phone && location.pathname !== '/team') - || (!isMobile.phone)) - && ((isMobile.phone && location.pathname !== '/consult') - || (!isMobile.phone)) - && ((isMobile.phone && location.pathname !== '/impact') - || (!isMobile.phone))) &&
} - - - - - - - - - - - - - - - } /> - - - - - - )} - /> + - ); } diff --git a/src/imports/analytics/index.js b/src/imports/analytics/index.js index 32ad76f..e7ccd56 100644 --- a/src/imports/analytics/index.js +++ b/src/imports/analytics/index.js @@ -58,3 +58,7 @@ export const trackEvent = (eventName, props = {}) => { ...props }); }; + +export const trackPage = (pageName, props = {}) => { + window.analytics.page(pageName, props); +}; diff --git a/src/imports/components/interactions/FullPageScroll.js b/src/imports/components/interactions/FullPageScroll.js new file mode 100644 index 0000000..6b3594c --- /dev/null +++ b/src/imports/components/interactions/FullPageScroll.js @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactFullpage from '@fullpage/react-fullpage'; + +export default class FullPageScroll extends React.Component { + render() { + const {children} = this.props; + return ( + ( + + {children} + + )} + /> + ); + } +} + +FullPageScroll.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/src/imports/utils/sendContactMessageToSlack.js b/src/imports/utils/sendContactMessageToSlack.js new file mode 100644 index 0000000..41096e7 --- /dev/null +++ b/src/imports/utils/sendContactMessageToSlack.js @@ -0,0 +1,21 @@ +import request from 'utils/request'; + +const WEBHOOK_URL = 'https://hooks.slack.com/services/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'; +const DEV_WEBHOOK_URL = 'https://hooks.slack.com/services/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'; + +const sendContactMessageToSlack = ({name, email, message}) => { + const formData = JSON.stringify({ + attachments: [ + { + color: '#000000', + pretext: 'New Contact Us form request', + author_name: `From: ${name}`, + text: `*Email:* ${email}\n*Message:* _${message}_`, + } + ] + }); + + return request.post(process.env.NODE_ENV === 'development' ? DEV_WEBHOOK_URL : WEBHOOK_URL, formData); +}; + +export default sendContactMessageToSlack; diff --git a/src/mobilePages/Journey/CaseStudies/index.js b/src/mobilePages/Journey/CaseStudies/index.js index a06df3b..b54ee65 100644 --- a/src/mobilePages/Journey/CaseStudies/index.js +++ b/src/mobilePages/Journey/CaseStudies/index.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; +import FullPageScroll from 'components/interactions/FullPageScroll'; import CaseStudy from './Sections/CaseStudy'; @@ -16,44 +16,36 @@ class MobileCaseStudies extends Component {

Our Work

- ( - -
- -
-
- -
-
- -
-
- -
-
- )} - /> + +
+ +
+
+ +
+
+ +
+
+ +
+
); } diff --git a/src/mobilePages/Journey/Consultation/Sections/ConsultForm/index.js b/src/mobilePages/Journey/Consultation/Sections/ConsultForm/index.js index 12ab3e0..13fa626 100644 --- a/src/mobilePages/Journey/Consultation/Sections/ConsultForm/index.js +++ b/src/mobilePages/Journey/Consultation/Sections/ConsultForm/index.js @@ -1,13 +1,10 @@ import React, {Component} from 'react'; -import request from 'utils/request'; import {NotificationContainer, NotificationManager} from 'react-notifications'; import 'react-notifications/lib/notifications.css'; import './index.styl'; - -const WEBHOOK_URL = 'https://hooks.slack.com/services/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'; -const DEV_WEBHOOK_URL = 'https://hooks.slack.com/services/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'; +import sendContactMessageToSlack from 'utils/sendContactMessageToSlack'; class Intro extends Component { constructor(props) { @@ -24,22 +21,9 @@ class Intro extends Component { handleSubmit(e) { e.preventDefault(); - const { - name, email, message - } = this.state; - - const formData = JSON.stringify({ - attachments: [ - { - color: '#000000', - pretext: 'New Contact Us form request', - author_name: `From: ${name}`, - text: `*Email:* ${email}\n*Message:* _${message}_`, - } - ] - }); + const {name, email, message} = this.state; - request.post(process.env.NODE_ENV === 'development' ? DEV_WEBHOOK_URL : WEBHOOK_URL, formData) + sendContactMessageToSlack({name, email, message}) .then(() => { this.setState({ name: '', diff --git a/src/mobilePages/Journey/Consultation/index.js b/src/mobilePages/Journey/Consultation/index.js index cba3634..853a962 100644 --- a/src/mobilePages/Journey/Consultation/index.js +++ b/src/mobilePages/Journey/Consultation/index.js @@ -1,9 +1,9 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; - +import FullPageScroll from 'components/interactions/FullPageScroll'; +// TODO rename all the `Sections` directories to `components` import Intro from './Sections/Intro'; import ConsultForm from './Sections/ConsultForm'; @@ -13,26 +13,18 @@ class MobileConsultation extends Component { render() { return (
- ( - -
- - -
-
- -
-
- -
-
- )} - /> + +
+ + +
+
+ +
+
+ +
+
); } diff --git a/src/mobilePages/Journey/Home/index.js b/src/mobilePages/Journey/Home/index.js index fe6343e..94b4227 100644 --- a/src/mobilePages/Journey/Home/index.js +++ b/src/mobilePages/Journey/Home/index.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; +import FullPageScroll from 'components/interactions/FullPageScroll'; import Intro from './Sections/Intro'; import Services from './Sections/Services'; @@ -15,55 +15,47 @@ class MobileHome extends Component { return (
- ( - -
- -
-
- -
-
-

- Our Work -

-
- -
-
- -
-
- -
-
-
- -
-
- )} - /> + +
+ +
+
+ +
+
+

+ Our Work +

+
+ +
+
+ +
+
+ +
+
+
+ +
+
); } diff --git a/src/mobilePages/Journey/Impact/index.js b/src/mobilePages/Journey/Impact/index.js index 5b6977d..73dfdf7 100644 --- a/src/mobilePages/Journey/Impact/index.js +++ b/src/mobilePages/Journey/Impact/index.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; +import FullPageScroll from 'components/interactions/FullPageScroll'; import Intro from './Sections/Intro'; @@ -13,22 +13,14 @@ class MobileImpact extends Component { return (
- ( - -
- -
-
- -
-
- )} - /> + +
+ +
+
+ +
+
); } diff --git a/src/mobilePages/Journey/Services/index.js b/src/mobilePages/Journey/Services/index.js index 4460391..7683778 100644 --- a/src/mobilePages/Journey/Services/index.js +++ b/src/mobilePages/Journey/Services/index.js @@ -1,11 +1,10 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; +import FullPageScroll from 'components/interactions/FullPageScroll'; -import {FeatherPen, Compass, BrowserWindows} from 'icons'; - +import {BrowserWindows, Compass, FeatherPen} from 'icons'; import Intro from './Sections/Intro'; import IntroSecond from './Sections/IntroSecond'; @@ -18,64 +17,56 @@ class MobileServices extends Component { return (
- ( - -
- -
-
- -
-
-

- Dream, Design, Develop -

-

- We are always looking to hear from people that want to change the world with software. - We call them heroes. If you’re one of them, please get in touch. -

-
- + +
+
+ +
+
+

+ Dream, Design, Develop +

+

+ We are always looking to hear from people that want to change the world with software. + We call them heroes. If you’re one of them, please get in touch. +

+
+ } - /> -
-
- + } - /> -
-
- + } - /> -
-
-
- -
- - )} - /> + icon={} + /> +
+
+
+ +
+ ); } diff --git a/src/mobilePages/Journey/Team/index.js b/src/mobilePages/Journey/Team/index.js index 14b1046..5863a89 100644 --- a/src/mobilePages/Journey/Team/index.js +++ b/src/mobilePages/Journey/Team/index.js @@ -1,8 +1,8 @@ import React, {Component} from 'react'; -import ReactFullpage from '@fullpage/react-fullpage'; import MobileHeader from 'components/MobileHeader'; import MobileFooter from 'components/MobileFooter'; +import FullPageScroll from 'components/interactions/FullPageScroll'; import Intro from './Sections/Intro'; import TeamMember from './Sections/TeamMember'; @@ -65,34 +65,26 @@ class MobileTeam extends Component { return (
- ( - -
- -
- {teamMembers.map((member => ( -
- -
- )))} -
- -
-
- )} - /> + +
+ +
+ {teamMembers.map((member => ( +
+ +
+ )))} +
+ +
+
); } diff --git a/src/pages/Journey/Consultation/components/ConsultationForm.js b/src/pages/Journey/Consultation/components/ConsultationForm.js new file mode 100644 index 0000000..c735812 --- /dev/null +++ b/src/pages/Journey/Consultation/components/ConsultationForm.js @@ -0,0 +1,85 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Input from 'components/Input'; +import TextArea from 'components/TextArea'; +import Button from 'components/interactions/Button'; +import {trackEvent} from 'analytics'; + +const EMPTY_VALUES = { + name: '', + email: '', + message: '', +}; + +export default class ConsultationForm extends React.Component { + state = { + ...EMPTY_VALUES, + submitting: false, + pristine: true + }; + + onFormInputChange(e) { + const {pristine} = this.state; + if (pristine) { + this.setState({pristine: false}, () => trackEvent('Consult form touched')); + } + this.setState({[e.target.name]: e.target.value}); + } + + onSubmit(e) { + e.preventDefault(); + + const {onSubmit} = this.props; + this.setState({submitting: true}); + + onSubmit(this.state) + .then(() => this.setState(EMPTY_VALUES)) + .then(() => trackEvent('Consult form submitted')) + .finally(() => this.setState({submitting: false})); + } + + render() { + const { + name, email, message, submitting + } = this.state; + return ( +
this.onSubmit(e)}> + this.onFormInputChange(e)} + /> + this.onFormInputChange(e)} + /> +
+