├── netlify.toml ├── server ├── .eslintignore ├── webpack.config.js ├── Procfile ├── .babelrc ├── .prettierrc.json ├── src │ ├── api │ │ ├── index.js │ │ ├── resources │ │ │ ├── email │ │ │ │ ├── index.js │ │ │ │ ├── email.restRouter.js │ │ │ │ └── email.controller.js │ │ │ ├── trip │ │ │ │ ├── index.js │ │ │ │ ├── trip.restRouter.js │ │ │ │ └── trip.model.js │ │ │ ├── user │ │ │ │ ├── index.js │ │ │ │ ├── user.restRouter.js │ │ │ │ └── user.controller.js │ │ │ ├── subscribe │ │ │ │ ├── index.js │ │ │ │ ├── subscribe.restRouter.js │ │ │ │ └── subscribe.controller.js │ │ │ └── waypoint │ │ │ │ ├── index.js │ │ │ │ ├── waypoint.restRouter.js │ │ │ │ └── waypoint.model.js │ │ ├── modules │ │ │ ├── public.js │ │ │ └── email.js │ │ └── restRouter.js │ ├── config │ │ ├── prod.js │ │ ├── testing.js │ │ └── index.js │ ├── server.js │ ├── db.js │ └── index.js ├── .gitignore ├── public │ └── images │ │ └── marker.png ├── tests │ ├── modules │ │ ├── testServer.js │ │ ├── testPublic.js │ │ ├── testSettings.js │ │ ├── testSubscribe.js │ │ └── testUser.js │ ├── setup.js │ └── mock.js ├── .eslintrc.json ├── README.md ├── webpack.prod.js ├── webpack.dev.js └── package.json ├── .gitignore ├── client ├── public │ ├── _redirects │ ├── favicon.ico │ ├── images │ │ ├── AJ.png │ │ ├── JC.png │ │ ├── VM.png │ │ ├── bg.jpg │ │ ├── AB1.png │ │ ├── UJ1.png │ │ ├── Thuy1.png │ │ ├── pear.jpeg │ │ ├── bkwdslogo.png │ │ ├── phoneimage.png │ │ ├── features-list.png │ │ ├── features-plan.png │ │ ├── features-share.png │ │ ├── features-track.png │ │ ├── hikerscontent.png │ │ ├── hikerscontent2.png │ │ └── map_placeholder.gif │ ├── fonts │ │ ├── Wals-Light.otf │ │ ├── Wals-Medium.otf │ │ ├── Wals-Regular.otf │ │ ├── Wals-Light-Oblique.otf │ │ ├── Wals-Medium-Oblique.otf │ │ └── Wals-Regular-Oblique.otf │ ├── manifest.json │ └── index.html ├── .prettierrc.json ├── src │ ├── components │ │ ├── Maps │ │ │ ├── SingleTrip │ │ │ │ ├── mapRenderUtil.js │ │ │ │ ├── TripInfo.js │ │ │ │ ├── Waypoint.js │ │ │ │ └── mapUtil.js │ │ │ ├── Autocomplete.js │ │ │ └── MobileMapPanel.js │ │ ├── NewTrip.js │ │ ├── Landing.js │ │ ├── Settings.js │ │ ├── Register.js │ │ ├── Login.js │ │ ├── Breadcrumb.js │ │ ├── Billing │ │ │ ├── StripeProvider.js │ │ │ ├── Pending.js │ │ │ ├── index.js │ │ │ ├── Invoices.js │ │ │ ├── AccountType.js │ │ │ └── PaymentDetails.js │ │ ├── EditTrip.js │ │ ├── icons │ │ │ ├── AddSvg.js │ │ │ ├── orange-marker.svg │ │ │ ├── black-marker.svg │ │ │ ├── green-marker.svg │ │ │ ├── FlagSvg.js │ │ │ ├── AddButton.js │ │ │ ├── SaveSvg.js │ │ │ ├── DeleteIcon.js │ │ │ ├── ChevronSvg.js │ │ │ ├── EditSvg.js │ │ │ ├── UserSvg.js │ │ │ ├── GitHubSvg.js │ │ │ ├── Puff.js │ │ │ ├── GoogleIcon.js │ │ │ └── ChartSvg.js │ │ ├── UnauthenticatedLinks.js │ │ ├── AuthenticatedLinks.js │ │ ├── AddTripButton.js │ │ ├── LandingPage │ │ │ ├── MobileMenu.js │ │ │ ├── CallToAction.js │ │ │ ├── LandingPageNav.js │ │ │ ├── FooterContent.js │ │ │ ├── Plans.js │ │ │ ├── index.js │ │ │ ├── Button.js │ │ │ ├── Hero.js │ │ │ ├── PlansCard.js │ │ │ ├── Footer.js │ │ │ └── Typewriter.js │ │ ├── TripCardLoader.js │ │ ├── Root.js │ │ ├── PublicTripCard.js │ │ ├── pages │ │ │ ├── Pages.js │ │ │ └── Dashboard.js │ │ ├── Banner.js │ │ ├── propTypes.js │ │ ├── AppContainer.js │ │ ├── Trips.js │ │ ├── Breadcrumbs.js │ │ ├── ArchivedTrips.js │ │ ├── forms │ │ │ ├── WaypointForm.js │ │ │ ├── customInputs.js │ │ │ ├── formValidations.js │ │ │ ├── RecoverPassword.js │ │ │ └── SettingsForm.js │ │ ├── PublicTrips.js │ │ ├── CopyTripLinkModal.js │ │ ├── Trip.js │ │ ├── Modals │ │ │ └── Modal.js │ │ └── Dropdown.js │ ├── styles │ │ ├── Trip.styles.js │ │ ├── AuthenticatedLinks.styles.js │ │ ├── Landing.styles.js │ │ ├── Billing.styles.js │ │ ├── Explore.styles.js │ │ ├── Register.styles.js │ │ ├── Login.styles.js │ │ ├── Settings.styles.js │ │ ├── CreateTrip.styles.js │ │ ├── NewTrip.styles.js │ │ ├── Dashboard.styles.js │ │ ├── CheckoutForm.styles.js │ │ ├── Banner.styles.js │ │ ├── Sidebar.styles.js │ │ ├── AddTripButton.styles.js │ │ ├── Dropdown.styles.js │ │ ├── TripCard.styles.js │ │ ├── TripPicturesStyles.js │ │ └── theme │ │ │ ├── variables.js │ │ │ └── GlobalStyles.js │ ├── redux │ │ ├── actions │ │ │ ├── navigation.js │ │ │ ├── modal.js │ │ │ └── settings.js │ │ └── reducers │ │ │ ├── navigation.js │ │ │ ├── modal.js │ │ │ ├── settings.js │ │ │ ├── billing.js │ │ │ └── auth.js │ ├── config │ │ ├── firebase.js │ │ └── index.js │ ├── test │ │ └── App.test.js │ ├── index.js │ ├── utils │ │ ├── CustomRoute.js │ │ └── index.js │ └── store.js ├── .gitignore ├── .eslintrc.json └── package.json ├── scripts └── test ├── mkdocs.yml ├── .travis.yml ├── package.json ├── docs └── Auth.md └── pull_request_template.md /netlify.toml: -------------------------------------------------------------------------------- 1 | base = "client" -------------------------------------------------------------------------------- /server/.eslintignore: -------------------------------------------------------------------------------- 1 | /dist -------------------------------------------------------------------------------- /server/webpack.config.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/Procfile: -------------------------------------------------------------------------------- 1 | web: node dist/server.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | .env 4 | -------------------------------------------------------------------------------- /client/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /server/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /client/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false 3 | } 4 | -------------------------------------------------------------------------------- /client/src/components/Maps/SingleTrip/mapRenderUtil.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false 3 | } 4 | -------------------------------------------------------------------------------- /server/src/api/index.js: -------------------------------------------------------------------------------- 1 | export { restRouter } from "./restRouter" 2 | -------------------------------------------------------------------------------- /server/src/api/resources/email/index.js: -------------------------------------------------------------------------------- 1 | export * from "./email.restRouter" 2 | -------------------------------------------------------------------------------- /server/src/api/resources/trip/index.js: -------------------------------------------------------------------------------- 1 | export * from "./trip.restRouter" 2 | -------------------------------------------------------------------------------- /server/src/api/resources/user/index.js: -------------------------------------------------------------------------------- 1 | export * from "./user.restRouter" 2 | -------------------------------------------------------------------------------- /server/src/api/resources/subscribe/index.js: -------------------------------------------------------------------------------- 1 | export * from "./subscribe.restRouter" 2 | -------------------------------------------------------------------------------- /server/src/api/resources/waypoint/index.js: -------------------------------------------------------------------------------- 1 | export * from "./waypoint.restRouter" 2 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | *.error 4 | .webpack 5 | 6 | .env 7 | dist/ 8 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/images/AJ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/AJ.png -------------------------------------------------------------------------------- /client/public/images/JC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/JC.png -------------------------------------------------------------------------------- /client/public/images/VM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/VM.png -------------------------------------------------------------------------------- /client/public/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/bg.jpg -------------------------------------------------------------------------------- /client/public/images/AB1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/AB1.png -------------------------------------------------------------------------------- /client/public/images/UJ1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/UJ1.png -------------------------------------------------------------------------------- /client/public/images/Thuy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/Thuy1.png -------------------------------------------------------------------------------- /client/public/images/pear.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/pear.jpeg -------------------------------------------------------------------------------- /server/public/images/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/server/public/images/marker.png -------------------------------------------------------------------------------- /client/public/fonts/Wals-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Light.otf -------------------------------------------------------------------------------- /client/public/images/bkwdslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/bkwdslogo.png -------------------------------------------------------------------------------- /client/public/fonts/Wals-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Medium.otf -------------------------------------------------------------------------------- /client/public/fonts/Wals-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Regular.otf -------------------------------------------------------------------------------- /client/public/images/phoneimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/phoneimage.png -------------------------------------------------------------------------------- /client/public/images/features-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/features-list.png -------------------------------------------------------------------------------- /client/public/images/features-plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/features-plan.png -------------------------------------------------------------------------------- /client/public/images/features-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/features-share.png -------------------------------------------------------------------------------- /client/public/images/features-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/features-track.png -------------------------------------------------------------------------------- /client/public/images/hikerscontent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/hikerscontent.png -------------------------------------------------------------------------------- /client/public/images/hikerscontent2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/hikerscontent2.png -------------------------------------------------------------------------------- /client/public/fonts/Wals-Light-Oblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Light-Oblique.otf -------------------------------------------------------------------------------- /client/public/images/map_placeholder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/images/map_placeholder.gif -------------------------------------------------------------------------------- /client/public/fonts/Wals-Medium-Oblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Medium-Oblique.otf -------------------------------------------------------------------------------- /client/public/fonts/Wals-Regular-Oblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BloomTech-Labs/LabsPT1_bkwds/HEAD/client/public/fonts/Wals-Regular-Oblique.otf -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | yarn 3 | cd client 4 | yarn 5 | yarn run lint 6 | 7 | cd .. 8 | cd server 9 | yarn 10 | yarn run lint 11 | yarn run test 12 | -------------------------------------------------------------------------------- /client/src/styles/Trip.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const TripStyles = styled.div` 4 | a { 5 | text-decoration: underline; 6 | } 7 | ` 8 | -------------------------------------------------------------------------------- /client/src/styles/AuthenticatedLinks.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const AuthenticatedLinksStyles = styled.ul` 4 | display: flex; 5 | align-items: center; 6 | ` 7 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Backwoods Tracker API 2 | nav: 3 | - Home: Auth.md 4 | - Authorization: Auth.md 5 | - User: User.md 6 | - Trip: Trip.md 7 | - Waypoint: Waypoint.md 8 | theme: readthedocs -------------------------------------------------------------------------------- /client/src/components/NewTrip.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import CreateTrip from "../components/Maps/CreateTrip" 3 | 4 | const NewTrip = () => { 5 | return 6 | } 7 | 8 | export default NewTrip 9 | -------------------------------------------------------------------------------- /client/src/redux/actions/navigation.js: -------------------------------------------------------------------------------- 1 | import { TOGGLE_SIDEBAR } from "./types" 2 | 3 | export const toggleSidebar = isSidebarOpen => dispatch => { 4 | dispatch({ type: TOGGLE_SIDEBAR, payload: isSidebarOpen }) 5 | } 6 | -------------------------------------------------------------------------------- /client/src/redux/actions/modal.js: -------------------------------------------------------------------------------- 1 | import { OPEN_MODAL, CLOSE_MODAL } from "./types" 2 | 3 | export const openModal = () => ({ 4 | type: OPEN_MODAL 5 | }) 6 | 7 | export const closeModal = () => ({ 8 | type: CLOSE_MODAL 9 | }) 10 | -------------------------------------------------------------------------------- /server/src/config/prod.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | port: process.env.PORT, 3 | db: { 4 | url: process.env.MONGO_URI 5 | }, 6 | stripe: { 7 | instance: require("stripe")(process.env.STRIPE_KEY_SERVER_PROD) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/src/config/testing.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | port: process.env.PORT || 5000, 3 | db: { 4 | url: "mongodb://127.0.0.1/backwoods" 5 | }, 6 | stripe: { 7 | instance: require("stripe")(process.env.STRIPE_KEY_SERVER_TEST) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/styles/Landing.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const LandingStyles = styled.div` 4 | height: 200vh; 5 | background: url(./images/pear.jpeg); 6 | .trash { 7 | position: fixed; 8 | bottom: 0; 9 | } 10 | ` 11 | -------------------------------------------------------------------------------- /client/src/styles/Billing.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const BillingStyles = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | margin-top: 30px; 7 | padding: 0px; 8 | align-items: center; 9 | width: 100%; 10 | ` 11 | -------------------------------------------------------------------------------- /client/src/styles/Explore.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | import { media } from "./theme/mixins" 4 | 5 | export const ExploreHeader = styled.h4` 6 | margin: 40px 0 20px 50px; 7 | 8 | ${media.phone` 9 | margin: 40px 0 20px 60px; 10 | `} 11 | ` 12 | -------------------------------------------------------------------------------- /client/src/components/Landing.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import * as s from "../styles/Landing.styles" 3 | 4 | const Landing = () => { 5 | return ( 6 | 7 |
LANDING PAGE
8 |
9 | ) 10 | } 11 | 12 | export default Landing 13 | -------------------------------------------------------------------------------- /client/src/config/firebase.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app" 2 | import "firebase/auth" 3 | 4 | import { FirebaseConfig } from "../config/index" 5 | firebase.initializeApp(FirebaseConfig) 6 | 7 | export const authRef = firebase.auth() 8 | export const provider = new firebase.auth.GoogleAuthProvider() 9 | -------------------------------------------------------------------------------- /client/src/test/App.test.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom" 3 | import App from "../components/App" 4 | 5 | it("renders without crashing", () => { 6 | const div = document.createElement("div") 7 | ReactDOM.render(, div) 8 | ReactDOM.unmountComponentAtNode(div) 9 | }) 10 | -------------------------------------------------------------------------------- /client/src/styles/Register.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const RegisterStyles = styled.div` 4 | height: 100%; 5 | width: 100%; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | justify-content: center; 10 | form { 11 | width: 300px; 12 | } 13 | ` 14 | -------------------------------------------------------------------------------- /server/tests/modules/testServer.js: -------------------------------------------------------------------------------- 1 | import request from "supertest" 2 | import app from "../../src/server" 3 | 4 | describe("Test server root path", () => { 5 | test("It should start and run without error", async () => { 6 | const response = await request(app).get("/") 7 | expect(response.statusCode).toBe(200) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /client/src/components/Settings.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import SettingsForm from "./forms/SettingsForm" 4 | import * as s from "../styles/Settings.styles" 5 | 6 | const Settings = () => ( 7 | 8 |

Change email / password

9 | 10 |
11 | ) 12 | 13 | export default Settings 14 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "bkwds.", 3 | "name": "bkwds.", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/src/components/Register.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import * as s from "../styles/Register.styles" 4 | import RegisterForm from "./forms/RegisterForm" 5 | 6 | const Register = () => { 7 | return ( 8 | 9 |

Sign up

10 | 11 |
12 | ) 13 | } 14 | 15 | export default Register 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "10" 5 | 6 | before_install: 7 | - chmod +x scripts/test 8 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0 9 | - export PATH="$HOME/.yarn/bin:$PATH" 10 | 11 | cache: 12 | yarn: true 13 | directories: 14 | - node_modules 15 | 16 | services: mongodb 17 | 18 | script: 19 | - scripts/test 20 | 21 | -------------------------------------------------------------------------------- /server/src/api/resources/email/email.restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import * as emailController from "./email.controller" 3 | 4 | export const emailRouter = express.Router() 5 | 6 | emailRouter.route("/user/:email").post(emailController.sendPasswordResetEmail) 7 | 8 | emailRouter 9 | .route("/receive_new_password/:userId/:token") 10 | .post(emailController.receiveNewPassword) 11 | -------------------------------------------------------------------------------- /client/src/styles/Login.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const LoginStyles = styled.div` 4 | height: 100%; 5 | width: 100%; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | justify-content: center; 10 | form { 11 | width: 300px; 12 | } 13 | input { 14 | width: 100%; 15 | } 16 | a { 17 | margin-top: 1rem; 18 | } 19 | ` 20 | -------------------------------------------------------------------------------- /server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true 5 | }, 6 | "plugins": ["prettier"], 7 | "extends": ["plugin:prettier/recommended"], 8 | "parser": "babel-eslint", 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "no-unused-vars": "error", 15 | "prettier/prettier": "error" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/redux/reducers/navigation.js: -------------------------------------------------------------------------------- 1 | import { TOGGLE_SIDEBAR } from "../actions/types" 2 | 3 | const defaultState = { 4 | isSidebarOpen: false 5 | } 6 | 7 | export const navigationReducer = (state = defaultState, action) => { 8 | switch (action.type) { 9 | case TOGGLE_SIDEBAR: 10 | return { ...state, isSidebarOpen: !action.payload } 11 | 12 | default: 13 | return state 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/src/components/Login.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "react-router-dom" 3 | 4 | import * as s from "../styles/Login.styles" 5 | import LoginForm from "./forms/LoginForm" 6 | 7 | const Login = () => ( 8 | 9 |

Log in

10 | 11 | Forgot your password? 12 |
13 | ) 14 | 15 | export default Login 16 | -------------------------------------------------------------------------------- /client/src/redux/reducers/modal.js: -------------------------------------------------------------------------------- 1 | import { OPEN_MODAL, CLOSE_MODAL } from "../actions/types" 2 | 3 | const defaultState = { 4 | isOpen: false 5 | } 6 | 7 | export const modalReducer = (state = defaultState, action) => { 8 | switch (action.type) { 9 | case OPEN_MODAL: 10 | return { ...state, isOpen: true } 11 | case CLOSE_MODAL: 12 | return { ...state, isOpen: false } 13 | default: 14 | return state 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/styles/Settings.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | 3 | export const SettingsStyles = styled.div` 4 | h4 { 5 | text-align: center; 6 | } 7 | 8 | .container { 9 | max-width: 100%; 10 | margin: 0 auto; 11 | display: flex; 12 | justify-content: flex-start; 13 | flex-wrap: wrap; 14 | } 15 | 16 | width: 100%; 17 | max-width: 400px; 18 | padding: 30px; 19 | float: none; 20 | margin: 0 auto; 21 | ` 22 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .env 16 | .DS_Store 17 | .env 18 | .env.local 19 | .env.development 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /server/src/server.js: -------------------------------------------------------------------------------- 1 | import { restRouter } from "./api" 2 | import dotenv from "dotenv" 3 | import express from "express" 4 | import cors from "cors" 5 | 6 | dotenv.config() 7 | const app = express() 8 | 9 | app.use(express.json({ limit: 4000000 })) 10 | app.use(cors()) 11 | app.use(express.static("public")) 12 | 13 | app.use("/api", restRouter) 14 | 15 | app.all("*", (req, res) => { 16 | res.json({ 17 | ok: true 18 | }) 19 | }) 20 | 21 | export default app 22 | -------------------------------------------------------------------------------- /server/src/db.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose" 2 | import config from "./config" 3 | 4 | mongoose.Promise = global.Promise 5 | 6 | export const connect = () => { 7 | return mongoose 8 | .connect( 9 | config.db.url, 10 | { userNewUrlParser: true } 11 | ) 12 | .then(() => { 13 | console.log("MONGO DB CONNECTED") 14 | }) 15 | .catch(err => { 16 | console.log(err) 17 | console.log(`Connection failed with config ${config.db.url}`) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /server/src/api/resources/user/user.restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import * as userController from "./user.controller" 3 | 4 | export const userRouter = express.Router() 5 | 6 | userRouter 7 | .route("/") 8 | .get(userController.getAllUsers) 9 | .post(userController.createUser) 10 | 11 | userRouter 12 | .route("/:id") 13 | .get(userController.getOneUser) 14 | .put(userController.updateUser) 15 | .delete(userController.deleteUser) 16 | 17 | userRouter.route("/:id/trips").get(userController.getUserTrips) 18 | -------------------------------------------------------------------------------- /server/src/index.js: -------------------------------------------------------------------------------- 1 | import { createServer } from "http" 2 | import { connect } from "./db" 3 | import config from "./config" 4 | import app from "./server" 5 | 6 | const server = createServer(app) 7 | let currentApp = app 8 | 9 | connect() 10 | server.listen(config.port, () => { 11 | console.log(`Server listening on port ${config.port}`) 12 | }) 13 | if (module.hot) { 14 | module.hot.accept(["./server"], () => { 15 | server.removeListener("request", currentApp) 16 | server.on("request", app) 17 | currentApp = app 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /client/src/components/Breadcrumb.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "react-router-dom" 3 | import PropTypes from "prop-types" 4 | 5 | const Breadcrumb = ({ linkpath, name, last }) => { 6 | return ( 7 |
  • 8 | 9 | {name} 10 | 11 |
  • 12 | ) 13 | } 14 | 15 | Breadcrumb.propTypes = { 16 | linkpath: PropTypes.string.isRequired, 17 | name: PropTypes.string.isRequired, 18 | last: PropTypes.bool 19 | } 20 | export default Breadcrumb 21 | -------------------------------------------------------------------------------- /client/src/components/Maps/SingleTrip/TripInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | export const TripInfo = props => { 5 | const trip = { props } 6 | const style = { 7 | padding: ".5rem", 8 | width: "30%", 9 | height: "35%", 10 | background: "white", 11 | position: "absolute", 12 | zIndex: 10, 13 | top: "1rem", 14 | right: "1rem" 15 | } 16 | return ( 17 |
    18 |

    {trip.name}

    19 |
    20 | ) 21 | } 22 | 23 | TripInfo.propTypes = { 24 | trip: PropTypes.object 25 | } 26 | -------------------------------------------------------------------------------- /client/src/components/Billing/StripeProvider.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connect } from "react-redux" 3 | import { StripeProvider } from "react-stripe-elements" 4 | import PropTypes from "prop-types" 5 | 6 | const StripeElementsContainer = ({ children, stripe }) => ( 7 | {children} 8 | ) 9 | 10 | StripeElementsContainer.propTypes = { 11 | stripe: PropTypes.object, 12 | children: PropTypes.element.isRequired 13 | } 14 | 15 | export default connect(({ billing }) => ({ stripe: billing.stripe }))( 16 | StripeElementsContainer 17 | ) 18 | -------------------------------------------------------------------------------- /client/src/components/EditTrip.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import * as s from "../styles/NewTrip.styles" 4 | import NewTripForm from "./forms/NewTripForm" 5 | 6 | const EditTrip = () => { 7 | return ( 8 | 9 |
    10 |

    Edit trip

    11 |
    {}
    12 |
    13 | trash 14 |
    15 |
    16 |
    17 | ) 18 | } 19 | 20 | export default EditTrip 21 | -------------------------------------------------------------------------------- /client/src/components/icons/AddSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const AddSvg = ({ width = "18px", height = "18px" }) => { 5 | return ( 6 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | AddSvg.propTypes = { 21 | width: PropTypes.string, 22 | height: PropTypes.string 23 | } 24 | 25 | export default AddSvg 26 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | Server for Backwoods Tracker 2 | 3 | ## Deployment 4 | 5 | We deploy to Heroku: https://backwoods-tracker.herokuapp.com/ 6 | 7 | _Note:_ Make sure you set the environment variables in the Heroku dashboard! `MONGO_URI` should be our production database. 8 | 9 | To deploy, cd into the server folder and run these commands: 10 | 11 | ```bash 12 | $ git init 13 | $ git remote add heroku https://git.heroku.com/backwoods-tracker.git 14 | ``` 15 | 16 | Double check that your remote is working by running `git remote -v`. You should see heroku pointing to our app on Heroku. 17 | 18 | Pushing is easy, just do `git push heroku master`. 19 | -------------------------------------------------------------------------------- /client/src/components/UnauthenticatedLinks.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "react-router-dom" 3 | import PropTypes from "prop-types" 4 | 5 | const UnauthenticatedLinks = ({ pathname }) => ( 6 | 14 | ) 15 | 16 | UnauthenticatedLinks.propTypes = { 17 | pathname: PropTypes.string.isRequired 18 | } 19 | 20 | export default UnauthenticatedLinks 21 | -------------------------------------------------------------------------------- /server/src/api/resources/subscribe/subscribe.restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import * as subscribeController from "./subscribe.controller" 3 | 4 | export const subscribeRouter = express.Router() 5 | const stripe = require("stripe")(process.env.STRIPE_KEY_SERVER_TEST) 6 | 7 | subscribeRouter 8 | .route("/invoices") 9 | .post((req, res) => subscribeController.retrieveInvoices(req, res, stripe)) 10 | 11 | subscribeRouter 12 | .route("/:id") 13 | .post((req, res) => subscribeController.subscribe(req, res, stripe)) 14 | 15 | subscribeRouter 16 | .route("/cancel/:id") 17 | .post((req, res) => subscribeController.cancel(req, res, stripe)) 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "eslint-config-prettier": "^3.3.0", 5 | "eslint-plugin-prettier": "^3.0.0", 6 | "eslint-plugin-react": "^7.11.1", 7 | "husky": "^1.2.0", 8 | "lint-staged": "^8.1.0", 9 | "prettier": "^1.15.3" 10 | }, 11 | "husky": { 12 | "hooks": { 13 | "pre-commit": "lint-staged" 14 | } 15 | }, 16 | "lint-staged": { 17 | "*.{js,json,css,md}": [ 18 | "prettier --write", 19 | "git add" 20 | ] 21 | }, 22 | "scripts": { 23 | "lint:client": "eslint client/**/*.js", 24 | "lint:server": "eslint server/**/*.js" 25 | }, 26 | "dependencies": {} 27 | } 28 | -------------------------------------------------------------------------------- /client/src/components/AuthenticatedLinks.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import PropTypes from "prop-types" 3 | 4 | import Dropdown from "./Dropdown" 5 | import * as s from "../styles/AuthenticatedLinks.styles" 6 | 7 | class AuthenticatedLinks extends Component { 8 | handleLogout = e => { 9 | e.preventDefault() 10 | this.props.logout() 11 | } 12 | 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | ) 19 | } 20 | } 21 | 22 | AuthenticatedLinks.propTypes = { 23 | logout: PropTypes.func.isRequired 24 | } 25 | 26 | export default AuthenticatedLinks 27 | -------------------------------------------------------------------------------- /client/src/styles/CreateTrip.styles.js: -------------------------------------------------------------------------------- 1 | import Styled from "styled-components" 2 | import { media } from "./theme/mixins" 3 | 4 | export const MapWrapper = Styled.div` 5 | position:relative; 6 | margin-left: -50px; 7 | overflow-x: hidden; 8 | overflow-y: hidden; 9 | ${media.tablet` 10 | margin-left: 0; 11 | `} 12 | 13 | height: 100%; 14 | 15 | #plus-icon { 16 | visibility: hidden; 17 | 18 | ${media.tablet` 19 | visibility: visible; 20 | cursor: pointer; 21 | z-index: 1; 22 | right: 40px; 23 | bottom: 200px; 24 | background: white; 25 | border-radius: 50%; 26 | position: absolute; 27 | `} 28 | } 29 | ` 30 | -------------------------------------------------------------------------------- /server/src/config/index.js: -------------------------------------------------------------------------------- 1 | import merge from "lodash.merge" 2 | 3 | const env = process.env.NODE_ENV 4 | 5 | const baseConfig = { 6 | port: process.env.PORT || 5000, 7 | secrets: { 8 | JWT_SECRET: process.env.JWT_SECRET 9 | }, 10 | db: { 11 | url: process.env.MONGO_URI 12 | } 13 | } 14 | 15 | let envConfig = {} 16 | 17 | switch (env) { 18 | case "development": 19 | case "dev": 20 | break 21 | case "test": 22 | case "testing": 23 | envConfig = require("./testing").config 24 | break 25 | case "prod": 26 | case "production": 27 | envConfig = require("./prod").config 28 | break 29 | default: 30 | } 31 | 32 | export default merge(baseConfig, envConfig) 33 | -------------------------------------------------------------------------------- /client/src/components/AddTripButton.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "react-router-dom" 3 | import PropTypes from "prop-types" 4 | import * as s from "../styles/AddTripButton.styles" 5 | 6 | const AddTripButton = ({ text }) => ( 7 | 8 |
    9 |
    10 | 11 |

    {text}

    12 | 13 | 14 |
    15 |
    16 |
    17 | ) 18 | 19 | AddTripButton.propTypes = { 20 | text: PropTypes.string.isRequired 21 | } 22 | 23 | export default AddTripButton 24 | -------------------------------------------------------------------------------- /client/src/components/icons/orange-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/src/components/icons/black-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path Copy 3 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /client/src/components/icons/green-marker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path Copy 4 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /server/src/api/resources/trip/trip.restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import * as tripController from "./trip.controller" 3 | 4 | export const tripRouter = express.Router() 5 | 6 | tripRouter 7 | .route("/") 8 | .get(tripController.getAllTrips) 9 | .post(tripController.createTrip) 10 | 11 | tripRouter.route("/repeat").post(tripController.repeatTrip) 12 | 13 | tripRouter 14 | .route("/:id") 15 | .get(tripController.getOneTrip) 16 | .put(tripController.updateTrip) 17 | .delete(tripController.deleteTrip) 18 | 19 | tripRouter.route("/:id/waypoints").get(tripController.populateWaypoints) 20 | 21 | tripRouter.route("/upload/:id").put(tripController.uploadPics) 22 | tripRouter.route("/pictures/:id").get(tripController.uploadPics) 23 | -------------------------------------------------------------------------------- /server/src/api/resources/waypoint/waypoint.restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import * as waypointController from "./waypoint.controller" 3 | 4 | export const waypointRouter = express.Router() 5 | 6 | waypointRouter 7 | .route("/") 8 | .get(waypointController.getAllWaypoints) 9 | .post(waypointController.createWaypoint) 10 | 11 | waypointRouter 12 | .route("/trip/:tripId") 13 | .get(waypointController.getWaypointsByTrip) 14 | .delete(waypointController.deleteWaypointsByTrip) 15 | 16 | waypointRouter.route("/batch").post(waypointController.createManyWaypoints) 17 | 18 | waypointRouter 19 | .route("/:id") 20 | .get(waypointController.getWaypoint) 21 | .put(waypointController.updateWaypoint) 22 | .delete(waypointController.deleteWaypoint) 23 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/MobileMenu.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connect } from "react-redux" 3 | import { push as Menu } from "react-burger-menu" 4 | import PropTypes from "prop-types" 5 | import { Link } from "react-router-dom" 6 | 7 | const MobileMenu = ({ isOpen }) => ( 8 | 15 | Log in 16 | Sign Up 17 | 18 | ) 19 | 20 | MobileMenu.propTypes = { 21 | isOpen: PropTypes.bool.isRequired 22 | } 23 | 24 | export default connect(({ navigation }) => ({ 25 | isOpen: navigation.isSidebarOpen 26 | }))(MobileMenu) 27 | -------------------------------------------------------------------------------- /client/src/components/icons/FlagSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const FlagSvg = ({ height = "32px", width = "32px", fill }) => { 5 | return ( 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | FlagSvg.propTypes = { 20 | height: PropTypes.string, 21 | width: PropTypes.string, 22 | fill: PropTypes.string 23 | } 24 | 25 | export default FlagSvg 26 | -------------------------------------------------------------------------------- /client/src/config/index.js: -------------------------------------------------------------------------------- 1 | // GENERAL 2 | export const APP_NAME = process.env.REACT_APP_NAME 3 | export const CLIENT_URI = process.env.REACT_APP_CLIENT_URI 4 | export const SERVER_URI = process.env.REACT_APP_SERVER_URI 5 | export const MAPS_KEY = process.env.REACT_APP_MAPS_KEY 6 | 7 | // STRIPE 8 | export const STRIPE_KEY = process.env.REACT_APP_STRIPE_KEY 9 | export const STRIPE_KEY_SERVER = process.env.REACT_APP_STRIPE_KEY_SERVER 10 | export const STRIPE_PLAN_ID_TEST = process.env.REACT_APP_STRIPE_PLAN_ID_TEST 11 | 12 | // OAUTH 13 | export const FB_APP_ID = process.env.REACT_APP_FB_APP_ID 14 | export const FirebaseConfig = { 15 | apiKey: process.env.REACT_APP_FIREBASE_API_KEY, 16 | authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, 17 | databaseURL: process.env.REACT_APP_FIREBASE_DB_URL 18 | } 19 | -------------------------------------------------------------------------------- /client/src/redux/reducers/settings.js: -------------------------------------------------------------------------------- 1 | import { 2 | INI_UPDATE_SETTINGS, 3 | UPDATE_SETTINGS_SUCCESS, 4 | UPDATE_SETTINGS_FAILURE 5 | } from "../actions/types" 6 | 7 | const defaultState = { 8 | pending: false, 9 | error: null 10 | } 11 | 12 | export const settingsReducer = (state = defaultState, action) => { 13 | switch (action.type) { 14 | case INI_UPDATE_SETTINGS: 15 | return { 16 | ...state, 17 | pending: true 18 | } 19 | case UPDATE_SETTINGS_SUCCESS: 20 | return { 21 | ...state, 22 | pending: false 23 | } 24 | 25 | case UPDATE_SETTINGS_FAILURE: 26 | return { 27 | ...state, 28 | pending: false, 29 | error: action.payload 30 | } 31 | 32 | default: 33 | return state 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/tests/modules/testPublic.js: -------------------------------------------------------------------------------- 1 | import request from "supertest" 2 | import app from "../../src/server" 3 | 4 | let tripId 5 | 6 | describe("Test public trip endpoints", () => { 7 | test("GET all public trips", done => { 8 | request(app) 9 | .get("/api/public/trips") 10 | .then(response => { 11 | tripId = response.body[1].id 12 | expect(response.statusCode).toBe(200) 13 | expect(response.body.length).toEqual(2) 14 | return done() 15 | }) 16 | }) 17 | test("Get single public trip", done => { 18 | request(app) 19 | .get(`/api/public/trips/${tripId}`) 20 | .then(response => { 21 | expect(response.statusCode).toBe(200) 22 | expect(response.body.isPublic).toBe(true) 23 | return done() 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "plugins": ["prettier", "react"], 8 | "extends": ["plugin:prettier/recommended", "plugin:react/recommended"], 9 | "parser": "babel-eslint", 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "ecmaVersion": 2018, 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | "array-callback-return": "off", 19 | "no-unused-vars": "error", 20 | "prettier/prettier": "error", 21 | "react/prop-types": 2, 22 | "react/no-unescaped-entities": 0, 23 | "react/prefer-es6-class": "error", 24 | "react/prefer-stateless-function": "error" 25 | }, 26 | "settings": { 27 | "react": { 28 | "version": "16.6.3" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/components/TripCardLoader.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ContentLoader from "react-content-loader" 3 | 4 | export const TripCardLoader = () => ( 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ) 21 | 22 | export default TripCardLoader 23 | -------------------------------------------------------------------------------- /server/tests/modules/testSettings.js: -------------------------------------------------------------------------------- 1 | import request from "supertest" 2 | import app from "../../src/server" 3 | 4 | import * as mock from "../mock" 5 | 6 | let token 7 | let email 8 | 9 | describe("Test Setting page", () => { 10 | beforeAll(async done => { 11 | const response = await request(app) 12 | .post("/api/login") 13 | .send({ email: mock.userOne.email, password: "testpass" }) 14 | email = mock.userOne.email 15 | token = response.body.token 16 | return done() 17 | }) 18 | test("POST change password", done => { 19 | request(app) 20 | .get("/api/changePassword") 21 | .set("Authorization", `Bearer ${token}`) 22 | .send({ email, oldPassword: "testpass", newPassword: "newTestPass" }) 23 | .then(response => { 24 | expect(response.statusCode).toBe(200) 25 | done() 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /client/src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Switch, Route } from "react-router-dom" 3 | 4 | import LandingPage from "./LandingPage/" 5 | import Dashboard from "./pages/Dashboard" 6 | import CustomRoute from "../utils/CustomRoute" 7 | import Pages from "./pages/Pages" 8 | import ActiveTrip from "./Maps/SingleTrip" 9 | 10 | const Root = () => ( 11 | 12 | 13 | ( 17 | 18 | )} 19 | /> 20 | 21 | 22 |
    404: Route not found
    } /> 23 | 24 |
    25 | ) 26 | export default Root 27 | -------------------------------------------------------------------------------- /server/src/api/modules/public.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import { Trip } from "../resources/trip/trip.model" 3 | 4 | const getAllPublicTrips = (req, res) => { 5 | Trip.find({ isPublic: true }) 6 | .then(trips => { 7 | res.status(200).json(trips) 8 | }) 9 | .catch(err => { 10 | res.status(500).json(err) 11 | }) 12 | } 13 | 14 | const getOnePublicTrip = (req, res) => { 15 | Trip.findOne({ _id: req.params.id }) 16 | .populate("waypoints") 17 | .exec() 18 | .then(trip => { 19 | if (trip.isPublic === false) { 20 | return res.status(401).json("Trip is not publicly available") 21 | } 22 | res.status(200).json(trip) 23 | }) 24 | .catch(err => { 25 | return res.status(500).send(err) 26 | }) 27 | } 28 | export const publicRouter = express.Router() 29 | 30 | publicRouter.route("/trips").get(getAllPublicTrips) 31 | publicRouter.route("/trips/:id").get(getOnePublicTrip) 32 | -------------------------------------------------------------------------------- /docs/Auth.md: -------------------------------------------------------------------------------- 1 | ## Register a new user 2 | 3 | Registers and creates a new user in the database 4 | 5 | **URL**: `/api/register/` 6 | 7 | **Method**: `POST` 8 | 9 | **Token required**: NO 10 | 11 | **Success Response**: 12 | 13 | - **Status Code**: `201 Created` 14 | 15 | --- 16 | 17 | ## Log in a user 18 | 19 | Returns the logged in user and authorization token 20 | 21 | **URL**: `/api/login/` 22 | 23 | **Method**: `POST` 24 | 25 | **Token required**: NO 26 | 27 | **Success Response**: 28 | 29 | - **Status Code**: `200 OK` 30 | 31 | **Example Content** 32 | 33 | ``` 34 | { 35 | "user": { 36 | "id": "5c32a56f83d4a923130752b2", 37 | "username": "Diddy", 38 | "email": "test@gmail.com", 39 | "subscribed": false 40 | }, 41 | "token": "e2JhbGciOgJIUzI1NiIsInR5dCI6IkpXVCJ9.eyJpZCI6IjVjMzJhNTZmODNkNGE5MjMxMzA#NTJihnIsImlhdCIhMTU0szAwNgUwNiwiZXhwIjoxNTQ3MDkxOTA2fQ.zb8M8jpVWDfxdK2Jum6iy-MCNQfyQNNaq_UpX3U5U6Q" 42 | } 43 | ``` 44 | 45 | --- 46 | -------------------------------------------------------------------------------- /client/src/styles/NewTrip.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import { media, flexCenterMixin } from "./theme/mixins" 3 | 4 | export const NewTripStyles = styled.div` 5 | .create-trip { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | .new-trip-form { 11 | z-index: 10000; 12 | position: relative; 13 | form { 14 | display: flex; 15 | flex-wrap: wrap; 16 | } 17 | 18 | .new-trip-form-field { 19 | ${flexCenterMixin}; 20 | } 21 | 22 | button { 23 | width: 10rem; 24 | } 25 | } 26 | 27 | ${media.tablet` 28 | input { 29 | width: 10rem; 30 | } 31 | `} 32 | ${media.phone` 33 | input { 34 | width: 100%; 35 | } 36 | `} 37 | 38 | .waypoint-form { 39 | form { 40 | display: flex; 41 | flex-wrap: wrap; 42 | } 43 | 44 | button { 45 | width: 10rem; 46 | } 47 | 48 | .waypoint-form-field { 49 | ${flexCenterMixin}; 50 | } 51 | } 52 | ` 53 | -------------------------------------------------------------------------------- /server/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | 3 | require("dotenv").config() 4 | 5 | module.exports = { 6 | entry: { 7 | app: "./src/index" 8 | }, 9 | mode: "production", 10 | watch: false, 11 | target: "node", 12 | node: { 13 | __filename: true, 14 | __dirname: true 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.js?$/, 20 | use: [ 21 | { 22 | loader: "babel-loader", 23 | options: { 24 | babelrc: false, 25 | presets: ["@babel/preset-env"], 26 | plugins: [ 27 | "@babel/plugin-transform-regenerator", 28 | "@babel/plugin-transform-runtime", 29 | "@babel/plugin-proposal-function-bind" 30 | ] 31 | } 32 | } 33 | ], 34 | exclude: /node_modules/ 35 | } 36 | ] 37 | }, 38 | plugins: [], 39 | output: { path: path.join(__dirname, "dist"), filename: "server.js" } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/components/Billing/Pending.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | import PuffIcon from "../icons/Puff" 5 | 6 | const Dimmer = styled.div` 7 | background: rgba(0, 0, 0, 0.25); 8 | top: 0; 9 | left: 0; 10 | height: 100vh; 11 | width: 100%; 12 | position: fixed; 13 | z-index: 8; 14 | ` 15 | 16 | const Spinner = styled.div` 17 | position: absolute; 18 | display: flex; 19 | flex-direction: column; 20 | align-self: center; 21 | justify-self: center; 22 | align-items: center; 23 | justify-content: center; 24 | width: 300px; 25 | height: 200px; 26 | z-index: 9; 27 | border-radius: 10px; 28 | box-shadow: 0px 8px 24px rgba(13, 13, 18, 0.04); 29 | background: white; 30 | 31 | span { 32 | margin-bottom: 20px; 33 | } 34 | ` 35 | 36 | const Pending = () => ( 37 | <> 38 | 39 | Please wait... 40 | 41 | 42 | 43 | 44 | ) 45 | 46 | export default Pending 47 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { render } from "react-dom" 3 | import { Provider } from "react-redux" 4 | import { ConnectedRouter } from "connected-react-router" 5 | import { ThemeProvider } from "styled-components" 6 | 7 | import "bootstrap/dist/css/bootstrap.min.css" 8 | import * as serviceWorker from "./serviceWorker" 9 | 10 | import Root from "./components/Root" 11 | import { store } from "./store" 12 | import { theme } from "./styles/theme/variables" 13 | import { history } from "./store" 14 | 15 | render( 16 | 17 | 18 | 19 | 20 | 21 | 22 | , 23 | document.getElementById("root") 24 | ) 25 | 26 | // If you want your app to work offline and load faster, you can change 27 | // unregister() to register() below. Note this comes with some pitfalls. 28 | // Learn more about service workers: http://bit.ly/CRA-PWA 29 | serviceWorker.unregister() 30 | -------------------------------------------------------------------------------- /server/src/api/modules/email.js: -------------------------------------------------------------------------------- 1 | import nodemailer from "nodemailer" 2 | 3 | export const transporter = nodemailer.createTransport({ 4 | service: "gmail", 5 | auth: { 6 | user: process.env.EMAIL_LOGIN, 7 | pass: process.env.EMAIL_PASSWORD 8 | } 9 | }) 10 | 11 | export const getPasswordResetURL = (user, token) => 12 | `http://localhost:3000/password/reset/${user._id}/${token}` 13 | 14 | export const resetPasswordTemplate = (user, url) => { 15 | const from = process.env.EMAIL_LOGIN 16 | const to = user.email 17 | const subject = "🌻 Backwoods Password Reset 🌻" 18 | const html = ` 19 |

    Hey ${user.displayName || user.email},

    20 |

    We heard that you lost your Backwoods password. Sorry about that!

    21 |

    But don’t worry! You can use the following link to reset your password:

    22 | ${url} 23 |

    If you don’t use this link within 1 hour, it will expire.

    24 |

    Do something outside today!

    25 |

    –Your friends at Backwoods

    26 | ` 27 | 28 | return { from, to, subject, html } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/api/restRouter.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | import { userRouter } from "./resources/user" 3 | import { tripRouter } from "./resources/trip" 4 | import { waypointRouter } from "./resources/waypoint" 5 | import { protect, register, login, changePassword } from "./modules/auth" 6 | import { subscribeRouter } from "./resources/subscribe" 7 | import { emailRouter } from "./resources/email" 8 | import { publicRouter } from "./modules/public" 9 | 10 | export const restRouter = express.Router() 11 | 12 | // Auth routes 13 | restRouter.route("/register").post(register) 14 | restRouter.route("/login").post(login) 15 | restRouter.route("/changePassword").post(protect, changePassword) 16 | 17 | // Resource routes 18 | restRouter.use("/users", protect, userRouter) 19 | restRouter.use("/trips", protect, tripRouter) 20 | restRouter.use("/waypoints", protect, waypointRouter) 21 | 22 | // Service routes 23 | restRouter.use("/subscribe", protect, subscribeRouter) 24 | restRouter.use("/reset_password", emailRouter) 25 | 26 | // Public route 27 | restRouter.use("/public", publicRouter) 28 | -------------------------------------------------------------------------------- /client/src/components/Maps/SingleTrip/Waypoint.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | import * as s from "./components" 5 | import DeleteIcon from "../../icons/DeleteIcon" 6 | 7 | const Waypoint = ({ i, isEditing, handleDelete, handleEdit, name }) => ( 8 | 9 | handleEdit(e, i)} 16 | /> 17 | 18 | handleDelete(i)} 22 | > 23 | 24 | 25 | 26 | ) 27 | 28 | Waypoint.propTypes = { 29 | handleDelete: PropTypes.func.isRequired, 30 | handleEdit: PropTypes.func.isRequired, 31 | i: PropTypes.number.isRequired, 32 | isEditing: PropTypes.bool.isRequired, 33 | name: PropTypes.string.isRequired 34 | } 35 | 36 | export default Waypoint 37 | -------------------------------------------------------------------------------- /client/src/components/icons/AddButton.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const AddButton = ({ addWaypoint }) => ( 5 | 13 | 14 | 15 | 20 | 25 | 26 | 27 | 28 | ) 29 | 30 | AddButton.propTypes = { 31 | addWaypoint: PropTypes.func.isRequired 32 | } 33 | 34 | export default AddButton 35 | -------------------------------------------------------------------------------- /client/src/utils/CustomRoute.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { connect } from "react-redux" 3 | import { Redirect, Route } from "react-router" 4 | 5 | import { addTokenToState } from "../redux/actions/auth" 6 | 7 | const CustomRoute = props => { 8 | const { isLoggedIn, protectedPath, checkedForToken, ...rest } = props 9 | 10 | // If not logged in and haven't checked for token yet, 11 | // try to query DB for user with token: 12 | if (!checkedForToken && !isLoggedIn) { 13 | props.addTokenToState() 14 | } 15 | 16 | if (isLoggedIn || !protectedPath) { 17 | return 18 | } 19 | 20 | if (protectedPath && !isLoggedIn) { 21 | return ( 22 | 28 | ) 29 | } 30 | } 31 | 32 | const mapStateToProps = state => ({ 33 | isLoggedIn: state.auth.isLoggedIn, 34 | checkedForToken: state.auth.checkedForToken 35 | }) 36 | 37 | const mapDispatchToProps = { addTokenToState } 38 | 39 | export default connect( 40 | mapStateToProps, 41 | mapDispatchToProps 42 | )(CustomRoute) 43 | -------------------------------------------------------------------------------- /client/src/styles/Dashboard.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import { media } from "../styles/theme/mixins" 3 | 4 | export const DashboardStyles = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | height: 100%; 10 | 11 | /* Modal styles: */ 12 | 13 | ${media.phone` 14 | 15 | form { 16 | padding: 0 0.5rem; 17 | border-radius: none; 18 | background: inherit; 19 | box-shadow: none; 20 | 21 | label, input { 22 | font-size: 0.825rem; 23 | } 24 | input { 25 | margin-bottom: 0.625rem; 26 | padding: 0.375rem 0.625rem; 27 | } 28 | 29 | button { 30 | width: 100%; 31 | } 32 | } 33 | 34 | `} 35 | 36 | /* h6 is only used to greet a first timer in the modal */ 37 | h6 { 38 | color: ${({ theme }) => theme.tertiary}; 39 | font-weight: 300; 40 | font-size: 1.5rem; 41 | ${media.tablet` 42 | font-size: 1.25rem; 43 | font-style: italic; 44 | `} 45 | ${media.phone` 46 | font-size: 1.125rem; 47 | text-align: center; 48 | `} 49 | } 50 | ` 51 | -------------------------------------------------------------------------------- /client/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export function isProtectedPath(pathname, pathArray) { 2 | return pathArray.reduce( 3 | (acc, curr) => (pathname === curr ? true : acc), 4 | false 5 | ) 6 | } 7 | 8 | export function* makeTaglineIterator(taglinesArray) { 9 | let count = 0 10 | while (count < Infinity) { 11 | yield taglinesArray[count++ % taglinesArray.length] 12 | } 13 | } 14 | 15 | export function validateEmail(string) { 16 | return !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(string) 17 | } 18 | 19 | export const formatDate = date => date.toISOString().split("T")[0] 20 | 21 | export const getToday = () => new Date() 22 | 23 | export const getTomorrow = () => 24 | (today => new Date(new Date().setDate(today.getDate() + 1)))(new Date()) 25 | 26 | export const convertMarkerToWaypoint = marker => ({ 27 | order: marker.index + 1, 28 | name: `Checkpoint ${marker.index}`, 29 | lat: marker.getPosition().lat(), 30 | lon: marker.getPosition().lng(), 31 | start: Date.now(), 32 | end: Date.now() 33 | }) 34 | 35 | export const scrollTo = target => 36 | document 37 | .getElementById(target) 38 | .scrollIntoView({ block: "start", behavior: "smooth" }) 39 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/CallToAction.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import ButtonCTA from "./ButtonCTA" 4 | 5 | const CallToAction = styled.div` 6 | display: grid; 7 | grid-template-columns: 1fr 1fr; 8 | height: 90%; 9 | width: 100%; 10 | margin-top: 42px; 11 | margin-right: 2rem; 12 | 13 | h1 { 14 | color: white !important; 15 | text-shadow: 0.5px 0.5px 0.5px #000000; 16 | } 17 | div { 18 | display: flex; 19 | flex-direction: column; 20 | align-self: center; 21 | justify-self: center; 22 | } 23 | .accent { 24 | color: #f26a21 !important; 25 | } 26 | 27 | a { 28 | align-self: center; 29 | } 30 | ` 31 | 32 | const LandingCTA = () => { 33 | return ( 34 | 35 |
    36 |

    The companion app for

    37 |

    38 | + 39 | hiking 40 |

    41 |

    42 | + 43 | mountain climbing 44 |

    45 | 46 |
    47 |
    48 | ) 49 | } 50 | 51 | export default LandingCTA 52 | -------------------------------------------------------------------------------- /client/src/components/icons/SaveSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const SaveSvg = ({ width = "20px", height = "20px" }) => { 5 | return ( 6 | 14 | 15 | 19 | 24 | 25 | 26 | ) 27 | } 28 | 29 | SaveSvg.propTypes = { 30 | width: PropTypes.string, 31 | height: PropTypes.string 32 | } 33 | 34 | export default SaveSvg 35 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/LandingPageNav.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import { Link, NavLink } from "react-router-dom" 4 | 5 | const NavigationMenu = styled.div` 6 | display: grid; 7 | grid-template-columns: 1fr 500px; 8 | height: 50px; 9 | width: 100%; 10 | margin-top: 42px; 11 | ` 12 | 13 | const Img = styled.img` 14 | width: 128px; 15 | margin-left: 90px; 16 | ` 17 | 18 | const Menu = styled.div` 19 | display: grid; 20 | grid-template-columns: repeat(4, 110px); 21 | margin-right: 5%; 22 | text-align: center; 23 | justify-content: space-around; 24 | 25 | a { 26 | font-size: 20px; 27 | color: white !important; 28 | text-decoration: none; 29 | } 30 | ` 31 | 32 | const Nav = () => { 33 | return ( 34 | 35 |
    36 | 37 |
    38 | 39 | Features 40 | About 41 | Log in 42 | Sign up 43 | 44 |
    45 | ) 46 | } 47 | 48 | export default Nav 49 | -------------------------------------------------------------------------------- /client/src/components/icons/DeleteIcon.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const DeleteIcon = props => { 5 | const { width, height } = props 6 | return ( 7 | 15 | 16 | 17 | 21 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | DeleteIcon.propTypes = { 33 | height: PropTypes.string, 34 | width: PropTypes.string 35 | } 36 | 37 | export default DeleteIcon 38 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/FooterContent.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | import ButtonCTA from "./Button" 5 | 6 | const ContentContainer = styled.div` 7 | display: flex; 8 | flex-direction: row; 9 | background-image: url(./images/hikerscontent2.png); 10 | background-size: cover; 11 | justify-content: center; 12 | height: 60vh; 13 | width: 100vw; 14 | ` 15 | 16 | const BrandedContent = styled.div` 17 | display: flex; 18 | flex-direction: column; 19 | align-self: center; 20 | justify-content: center; 21 | align-items: center; 22 | 23 | h3 { 24 | color: white !important; 25 | text-shadow: 1px 2px 5px rgba(0, 0, 0, 0.75); 26 | } 27 | 28 | a { 29 | color: white !important; 30 | } 31 | 32 | .accent { 33 | color: #f26a21 !important; 34 | font-weight: bold; 35 | } 36 | ` 37 | 38 | const FooterContent = () => ( 39 | 40 | 41 |

    42 | Explore without boundaries. 43 |

    44 | 45 |
    46 |
    47 | ) 48 | 49 | export default FooterContent 50 | -------------------------------------------------------------------------------- /client/src/styles/CheckoutForm.styles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import { media } from "./theme/mixins" 3 | 4 | export const CheckoutFormStyles = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | margin: 30px auto 0; 8 | width: 400px; 9 | 10 | input { 11 | margin-bottom: 10px; 12 | border: 1px solid #d1d5da; 13 | font-size: 0.95rem; 14 | } 15 | 16 | .stripe-card-input { 17 | height: 38px; 18 | margin-bottom: 10px; 19 | } 20 | 21 | .StripeElement { 22 | padding: 0.6rem 0.6rem; 23 | border-radius: 0.375rem; 24 | border: 1px solid #d1d5da; 25 | background-color: white; 26 | color: #f6f6f6; 27 | } 28 | 29 | .StripeElement--focus { 30 | outline: none; 31 | border-color: #1e306e; 32 | box-shadow: 0 0 0 1px #1e306e; 33 | } 34 | 35 | .form-city-state { 36 | display: grid; 37 | grid-template-columns: 2fr 1fr 1fr; 38 | grid-gap: 10px; 39 | width: 100%; 40 | 41 | input { 42 | width: inherit; 43 | } 44 | } 45 | 46 | ${media.phone` 47 | width: 300px; 48 | 49 | .input-button { 50 | width: 300px; 51 | float: none; 52 | margin: 10px auto; 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | } 57 | `} 58 | ` 59 | -------------------------------------------------------------------------------- /client/src/components/icons/ChevronSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import PropTypes from "prop-types" 4 | 5 | const ChevronSvg = ({ 6 | width = "1rem", 7 | height = "1rem", 8 | fill = "currentColor", 9 | transform 10 | }) => { 11 | const Icon = styled.span` 12 | width: ${width}; 13 | height: ${height}; 14 | fill: ${fill}; 15 | /* transform: ${transform}; */ 16 | 17 | position: relative; 18 | display: inline-block; 19 | vertical-align: middle; 20 | cursor: pointer; 21 | align-self: center; 22 | margin-top: 1px; 23 | 24 | svg { 25 | position: absolute; 26 | top: 1.5px; 27 | left: 1px; 28 | width: auto; 29 | height: 100%; 30 | transform: ${transform}; 31 | } 32 | ` 33 | 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | ChevronSvg.propTypes = { 45 | height: PropTypes.string, 46 | width: PropTypes.string, 47 | fill: PropTypes.string, 48 | transform: PropTypes.string 49 | } 50 | 51 | export default ChevronSvg 52 | -------------------------------------------------------------------------------- /client/src/components/icons/EditSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import PropTypes from "prop-types" 3 | 4 | const EditIcon = ({ width = "20px", height = "20px" }) => { 5 | return ( 6 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | EditIcon.propTypes = { 33 | width: PropTypes.string, 34 | height: PropTypes.string 35 | } 36 | 37 | export default EditIcon 38 | -------------------------------------------------------------------------------- /client/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, combineReducers } from "redux" 2 | import { connectRouter, routerMiddleware } from "connected-react-router" 3 | import { createBrowserHistory } from "history" 4 | import { composeWithDevTools } from "redux-devtools-extension" 5 | import thunk from "redux-thunk" 6 | import logger from "redux-logger" 7 | 8 | import { authReducer } from "./redux/reducers/auth" 9 | import { tripReducer } from "./redux/reducers/trips" 10 | import { billingReducer } from "./redux/reducers/billing" 11 | import { modalReducer } from "./redux/reducers/modal" 12 | import { settingsReducer } from "./redux/reducers/settings" 13 | import { navigationReducer } from "./redux/reducers/navigation" 14 | 15 | export const history = createBrowserHistory() 16 | 17 | const composeEnhancers = composeWithDevTools({ trace: true }) 18 | const middleware = [thunk, logger, routerMiddleware(history)] 19 | 20 | const createRootReducer = history => 21 | combineReducers({ 22 | auth: authReducer, 23 | trips: tripReducer, 24 | billing: billingReducer, 25 | modal: modalReducer, 26 | settings: settingsReducer, 27 | router: connectRouter(history), 28 | navigation: navigationReducer 29 | }) 30 | 31 | export const store = createStore( 32 | createRootReducer(history), 33 | composeEnhancers(applyMiddleware(...middleware)) 34 | ) 35 | -------------------------------------------------------------------------------- /client/src/components/icons/UserSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import PropTypes from "prop-types" 4 | import { theme } from "../../styles/theme/variables" 5 | 6 | const UserSvg = ({ height, width }) => { 7 | const Icon = styled.span` 8 | display: inline-block; 9 | cursor: pointer; 10 | height: ${height * 2}rem; 11 | width: ${width * 2}rem; 12 | text-align: center; 13 | 14 | svg { 15 | height: ${height}rem; 16 | width: ${width}rem; 17 | fill: ${theme.secondaryDark}; 18 | margin-top: 11px; 19 | } 20 | ` 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ) 31 | } 32 | 33 | UserSvg.propTypes = { 34 | height: PropTypes.string, 35 | width: PropTypes.string 36 | } 37 | 38 | export default UserSvg 39 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/Plans.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | import { media } from "../../styles/theme/mixins" 5 | import PlansCard from "./PlansCard" 6 | 7 | const Wrapper = styled.div` 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | height: 100vh; 13 | width: 100%; 14 | background: url(/images/hikerscontent.png); 15 | background-size: cover; 16 | 17 | h1 { 18 | color: white; 19 | text-align: left; 20 | text-shadow: 1px 2px 5px rgba(0, 0, 0, 0.75); 21 | font-size: 2.5rem; 22 | font-weight: 600; 23 | white-space: nowrap; 24 | overflow: visible; 25 | } 26 | 27 | ${media.tablet` 28 | height: 100%; 29 | padding: 10%; 30 | 31 | h1 { 32 | font-size: 1.75rem; 33 | } 34 | `} 35 | ` 36 | 37 | const Cards = styled.div` 38 | display: grid; 39 | grid-template-columns: 1fr 1fr; 40 | grid-gap: 5%; 41 | margin: 5% 0; 42 | 43 | ${media.tablet` 44 | grid-template-columns: 1fr; 45 | grid-template-rows: 1fr 1fr; 46 | `} 47 | ` 48 | 49 | const Plans = () => ( 50 | 51 |

    Choose the right plan for you

    52 | 53 | 54 | 55 | 56 |
    57 | ) 58 | 59 | export default Plans 60 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and a link to which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # How Has This Been Tested? 17 | 18 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration 19 | 20 | - [ ] Test A 21 | - [ ] Test B 22 | 23 | # Checklist: 24 | 25 | - [ ] My code follows the style guidelines of this project 26 | - [ ] I have performed a self-review of my own code 27 | - [ ] My code has been reviewed by at least one peer 28 | - [ ] I have commented my code, particularly in hard-to-understand areas 29 | - [ ] I have made corresponding changes to the documentation 30 | - [ ] My changes generate no new warnings 31 | - [ ] I have added tests that prove my fix is effective or that my feature works 32 | - [ ] New and existing unit tests pass locally with my changes 33 | -------------------------------------------------------------------------------- /client/src/components/icons/GitHubSvg.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | import PropTypes from "prop-types" 4 | 5 | const GitHubSvg = ({ width = "32px", height = "32px" }) => { 6 | const Icon = styled.a` 7 | svg { 8 | width: ${width}; 9 | height: ${height}; 10 | stroke: none; 11 | fill: rgba(0, 0, 0, 0.3); 12 | } 13 | ` 14 | 15 | return ( 16 | 21 | 22 | 30 | 31 | 32 | ) 33 | } 34 | 35 | GitHubSvg.propTypes = { 36 | width: PropTypes.string, 37 | height: PropTypes.string 38 | } 39 | 40 | export default GitHubSvg 41 | -------------------------------------------------------------------------------- /client/src/components/LandingPage/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import styled from "styled-components" 3 | import axios from "axios" 4 | import { SERVER_URI } from "../../config" 5 | 6 | import MobileMenu from "./MobileMenu" 7 | import Hero from "./Hero" 8 | import Features from "./Features" 9 | import Plans from "./Plans" 10 | import FooterContent from "./FooterContent" 11 | import Footer from "./Footer" 12 | import { fontDeclarations } from "../../styles/theme/mixins" 13 | 14 | const LandingPageContainer = styled.div` 15 | overflow: auto; 16 | height: 100%; 17 | ${fontDeclarations} 18 | font-family: Wals, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | 22 | .bm-item-list { 23 | padding: 40px 20px; 24 | } 25 | 26 | .bm-item { 27 | padding: 10px; 28 | font-size: 2rem; 29 | font-weight: 600; 30 | color: ${({ theme }) => theme.primary}; 31 | outline: none; 32 | } 33 | 34 | .bm-overlay { 35 | background: none !important; 36 | } 37 | ` 38 | 39 | class LandingPage extends Component { 40 | componentDidMount() { 41 | // WAKE UP HEROKU SERVER ON INITIAL PAGE LOAD 42 | axios.get(`${SERVER_URI}`) 43 | } 44 | render() { 45 | return ( 46 | 47 | 48 | 49 | 50 | 51 | 52 |