diff --git a/gatsby-browser.js b/gatsby-browser.js
index 3ace87e..0eecdb3 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -1 +1,7 @@
+import React from "react";
import "./src/styles/scss/index.scss";
+
+import { UserProvider } from "./src/context/UserContext";
+export const wrapRootElement = ({ element }) => (
+ {element}
+);
diff --git a/gatsby-node.js b/gatsby-node.js
index 6ab6215..0d581bf 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -3,7 +3,7 @@ const path = require("path");
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
- const ProductPage = path.resolve("./src/pages/book/_slug.tsx");
+ const ProductPage = path.resolve("./src/components/Product.tsx");
const result = await graphql(
`
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index 1333d7e..fdde46a 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,23 +1,27 @@
-import React, { useState } from "react";
+import React, { useContext, useState } from "react";
import { Link, navigate } from "gatsby";
+import UserContext from "../context/UserContext";
type NavbarProps = {
showBackButton?: boolean;
};
const Navbar = ({ showBackButton }: NavbarProps) => {
- const [token] = useState("");
+ const { user, setUser } = useContext(UserContext);
+ const [loading, setLoading] = useState(false);
const handleSignOut = async (event: any) => {
event.preventDefault();
+ setLoading(true);
try {
- await fetch(`/api/logout?token=${token}`, {
+ await fetch(`/api/logout?token=${user}`, {
method: "POST",
}).catch((err) => {
console.error(`Logout error: ${err}`);
});
} finally {
- window.localStorage.removeItem("google:tokens");
+ setLoading(false);
+ setUser("");
navigate("/");
}
};
@@ -37,13 +41,13 @@ const Navbar = ({ showBackButton }: NavbarProps) => {
- {!!token && (
+ {!!user && (
- Log out
+ {loading ? "Logging out" : "Log out"}
)}
diff --git a/src/components/PrivateRoute.tsx b/src/components/PrivateRoute.tsx
new file mode 100644
index 0000000..a2730d3
--- /dev/null
+++ b/src/components/PrivateRoute.tsx
@@ -0,0 +1,22 @@
+import React from "react";
+import Layout from "../components/templates/Layout";
+import { navigate } from "gatsby";
+import UserContext from "../context/UserContext";
+import { useContext } from "react";
+
+const PrivateRoute = ({ component: Component, ...rest }: any) => {
+ const { user } = useContext(UserContext);
+
+ if (!user) {
+ navigate(`/`);
+ return null;
+ }
+
+ return (
+
+
+
+ );
+};
+
+export default PrivateRoute;
diff --git a/src/pages/book/_slug.module.css b/src/components/Product.module.css
similarity index 100%
rename from src/pages/book/_slug.module.css
rename to src/components/Product.module.css
diff --git a/src/pages/book/_slug.tsx b/src/components/Product.tsx
similarity index 85%
rename from src/pages/book/_slug.tsx
rename to src/components/Product.tsx
index a2fb85f..566bb5d 100644
--- a/src/pages/book/_slug.tsx
+++ b/src/components/Product.tsx
@@ -1,31 +1,45 @@
-import React, { ReactPropTypes } from "react";
+import React, { ReactPropTypes, useState } from "react";
import { graphql, navigate } from "gatsby";
import { get } from "lodash";
import { renderRichText } from "gatsby-source-contentful/rich-text";
import { PaystackConsumer } from "react-paystack";
-import Layout from "../../components/templates/Layout";
-import StarRating from "../../components/StarRating";
+import StarRating from "./StarRating";
-import * as styles from "./_slug.module.css";
+import * as styles from "./Product.module.css";
+import Layout from "./templates/Layout";
+
+const generateReference = (): string => {
+ let text = "";
+ let possible =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ for (let i = 0; i < 10; i++)
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
+
+ return text;
+};
const payStackConfig = {
- reference: new Date().getTime().toString(),
- email: process.env.GATSBY_PAYSTACK_TEST_EMAIL || "",
- publicKey: process.env.GATSBY_PAYSTACK_PUBLIC_KEY || "",
+ email: process.env.GATSBY_PAYSTACK_TEST_EMAIL,
+ publicKey: process.env.GATSBY_PAYSTACK_PUBLIC_KEY,
};
const ProductPage = (props: ReactPropTypes) => {
const product = get(props, `data.contentfulProduct`);
+ const [reference, setReference] = useState(generateReference());
const handleSuccess = (_reference: string) => {
// TODO: send reference
navigate("/book/appointment");
};
- const handleClose = () => {};
+ const handleClose = (): void => {
+ setReference(generateReference());
+ };
const componentProps = {
+ reference,
...payStackConfig,
amount: product.price * 500 * 100,
text: "Book Sleep Appointment",
diff --git a/src/components/Products.tsx b/src/components/Products.tsx
new file mode 100644
index 0000000..09eff37
--- /dev/null
+++ b/src/components/Products.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import { graphql, Link, useStaticQuery } from "gatsby";
+import { ProductType } from "../types";
+import StarRating from "./StarRating";
+
+const ProductListPage = () => {
+ const productsList = useStaticQuery(graphql`
+ query {
+ allContentfulProduct {
+ nodes {
+ contentful_id
+ name
+ image {
+ file {
+ fileName
+ url
+ }
+ }
+ starrating
+ reviews
+ price
+ }
+ }
+ }
+ `);
+
+ return (
+
+
+ Pick your favorite mattress
+
+
+
+ {productsList.allContentfulProduct.nodes.map((product: ProductType) => (
+
+
+
+
+
+
+
+
+ {product.name}
+
+
+
+ ${product.price}
+ •
+
+
+
+
+
+ ))}
+
+
+ );
+};
+
+export default ProductListPage;
diff --git a/src/components/StarRating.tsx b/src/components/StarRating.tsx
index fc12341..42ed166 100644
--- a/src/components/StarRating.tsx
+++ b/src/components/StarRating.tsx
@@ -11,20 +11,18 @@ const StarRating = ({ stars, className }: StarRatingProp) => {
{[...Array(5)].map((_, index) => {
index += 1;
return (
- <>
-
-
-
- >
+
+
+
);
})}
diff --git a/src/components/templates/Layout/index.tsx b/src/components/templates/Layout/index.tsx
index e470936..335c001 100644
--- a/src/components/templates/Layout/index.tsx
+++ b/src/components/templates/Layout/index.tsx
@@ -2,7 +2,6 @@ import React from "react";
import { Helmet } from "react-helmet";
import Navbar from "../../Navbar";
import Footer from "../../Footer";
-import UserProvider from "../../../context/UserContext";
interface LayoutInterface {
children: React.ReactNode;
@@ -11,7 +10,7 @@ interface LayoutInterface {
}
const Layout = ({ children, showBackButton }: LayoutInterface) => (
-
+ <>
More sleep
@@ -19,7 +18,7 @@ const Layout = ({ children, showBackButton }: LayoutInterface) => (
{children}
-
+ >
);
export default Layout;
diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx
index 94243c0..378cbc4 100644
--- a/src/context/UserContext.tsx
+++ b/src/context/UserContext.tsx
@@ -10,7 +10,7 @@ interface UserProviderInterface {
children: React.ReactNode;
}
-export const UserContext = createContext({
+const UserContext = createContext({
user: "",
setUser: () => {},
});
@@ -24,4 +24,6 @@ const UserProvider = ({ children }: UserProviderInterface) => {
);
};
-export default UserProvider;
+export default UserContext;
+
+export { UserProvider };
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index ba21403..01c06ad 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -1,10 +1,16 @@
import { useState } from "react";
-function useLocalStorage(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] {
+function useLocalStorage(
+ key: string,
+ initialValue: T
+): [T, (value: T | ((val: T) => T)) => void] {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
+ if (typeof window === `undefined`) {
+ return;
+ }
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
@@ -21,7 +27,8 @@ function useLocalStorage(key: string, initialValue: T): [T, (value: T | ((val
const setValue = (value: T | ((val: T) => T)) => {
try {
// Allow value to be a function so we have same API as useState
- const valueToStore = value instanceof Function ? value(storedValue) : value;
+ const valueToStore =
+ value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
diff --git a/src/middleware/private-route.js b/src/middleware/private-route.js
index 1c668cb..1228e45 100644
--- a/src/middleware/private-route.js
+++ b/src/middleware/private-route.js
@@ -1,21 +1,10 @@
-import React from "react";
+import React, { useContext } from "react";
import { navigate } from "gatsby";
-
-const isBrowser = () => typeof window !== "undefined";
-
-const isLoggedIn = () => {
- const user = getUser();
- return !!user.access_token;
-};
-
-const getUser = () =>
- isBrowser() && window.localStorage.getItem("google:tokens")
- ? JSON.parse(window.localStorage.getItem("google:tokens"))
- : {};
+import UserContext from "../context/UserContext";
const PrivateRoute = ({ component: Component, location, ...rest }) => {
- console.log("private");
- if (!isLoggedIn() && location.pathname !== `/`) {
+ const { user } = useContext(UserContext);
+ if (!user) {
navigate("/");
return null;
}
diff --git a/src/pages/book/appointment.tsx b/src/pages/appointment.tsx
similarity index 92%
rename from src/pages/book/appointment.tsx
rename to src/pages/appointment.tsx
index d898b49..12cd266 100644
--- a/src/pages/book/appointment.tsx
+++ b/src/pages/appointment.tsx
@@ -2,7 +2,7 @@ import { navigate } from "gatsby";
import React from "react";
import { InlineWidget, CalendlyEventListener } from "react-calendly";
-import Layout from "../../components/templates/Layout";
+import Layout from "../components/templates/Layout";
const BookAppointmentPage = () => {
const handleChange = (e: any) => {
const {
diff --git a/src/pages/book.tsx b/src/pages/book.tsx
new file mode 100644
index 0000000..87a380d
--- /dev/null
+++ b/src/pages/book.tsx
@@ -0,0 +1,14 @@
+import React from "react";
+import { Router } from "@reach/router";
+import PrivateRoute from "../components/PrivateRoute";
+import ProductPage from "../components/Product";
+import ProductListPage from "../components/Products";
+
+const Book = () => (
+
+
+
+
+);
+
+export default Book;
diff --git a/src/pages/book/index.tsx b/src/pages/book/index.tsx
deleted file mode 100644
index 5ff7041..0000000
--- a/src/pages/book/index.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from "react";
-import { graphql, Link, useStaticQuery } from "gatsby";
-import { ProductType } from "../../types";
-import Layout from "../../components/templates/Layout";
-import StarRating from "../../components/StarRating";
-
-const ProductListPage = () => {
- const productsList = useStaticQuery(graphql`
- query {
- allContentfulProduct {
- nodes {
- contentful_id
- name
- image {
- file {
- fileName
- url
- }
- }
- starrating
- reviews
- price
- }
- }
- }
- `);
-
- return (
-
-
-
- Pick your favorite mattress
-
-
-
- {productsList.allContentfulProduct.nodes.map(
- (product: ProductType) => (
-
-
-
-
-
-
-
-
- {product.name}
-
-
-
- ${product.price}
- •
-
-
-
-
-
- )
- )}
-
-
-
- );
-};
-
-export default ProductListPage;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 83475bc..229dfc0 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,10 +1,13 @@
-import React, { useState } from "react";
+import React, { useContext, useState } from "react";
import Layout from "../components/templates/Layout";
import BgImage from "../assets/images/person-sleeping.jpg";
+import UserContext from "../context/UserContext";
+import { Link } from "gatsby";
const IndexPage = () => {
const [loading, setLoading] = useState(false);
+ const { user } = useContext(UserContext);
const handleSignIn = async (event: React.FormEvent) => {
event.preventDefault();
@@ -24,25 +27,35 @@ const IndexPage = () => {
Keep up with
- — sleep easy.
+ sleep
- You want more sleep, we know how to get it Book{" "}
+ You want more sleep, we know how to get it. Book{" "}
up to 12 hours of
- sleep and have a cozy place
- — prepared for you easy, anytime.
+ sleep and have a cozy place prepared for you anytime.
-
- {loading ? `Loading` : `Book a space`}
-
+ {user ? (
+
+
+ Back to dashboard
+
+
+ ) : (
+
+ {loading ? `Loading` : `Book a space`}
+
+ )}
diff --git a/src/pages/login.tsx b/src/pages/login.tsx
index c8f7ad4..dfe87a1 100644
--- a/src/pages/login.tsx
+++ b/src/pages/login.tsx
@@ -3,12 +3,12 @@ import { navigate } from "gatsby";
import { Helmet } from "react-helmet";
import qs from "query-string";
-import { UserContext } from "../context/UserContext";
+import UserContext from "../context/UserContext";
import Layout from "../components/templates/Layout";
const LoginPage = ({ location }: any) => {
const { token } = qs.parse(location?.search);
- const { setUser } = useContext(UserContext);
+ const { setUser, user } = useContext(UserContext);
useEffect(() => {
setUser(token);
diff --git a/src/pages/book/success.tsx b/src/pages/success.tsx
similarity index 92%
rename from src/pages/book/success.tsx
rename to src/pages/success.tsx
index 6cace6d..c86f7e2 100644
--- a/src/pages/book/success.tsx
+++ b/src/pages/success.tsx
@@ -1,7 +1,7 @@
import React from "react";
import { navigate } from "gatsby-link";
-import Layout from "../../components/templates/Layout";
+import Layout from "../components/templates/Layout";
const BookSuccessPage = () => {
const handleBack = () => {
diff --git a/src/styles/scss/_base.scss b/src/styles/scss/_base.scss
index 9e8674d..33201c0 100644
--- a/src/styles/scss/_base.scss
+++ b/src/styles/scss/_base.scss
@@ -2,7 +2,6 @@
box-sizing: border-box;
padding: 0;
margin: 0;
- text-transform: capitalize;
}
:root {