Skip to content

Commit

Permalink
divjoy pivot - UNTESTED
Browse files Browse the repository at this point in the history
  • Loading branch information
charltona committed Aug 15, 2020
1 parent c5d094e commit 7bd6f04
Show file tree
Hide file tree
Showing 94 changed files with 12,793 additions and 8,857 deletions.
27 changes: 24 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
/node_modules
/.idea
/.vs
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
5 changes: 5 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/locali.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

205 changes: 199 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,199 @@
# Installation
1. Check out repo
2. npm install
3. npm start => http://localhost:3000

test push
## 👉 Get Started

Install dependencies

```
npm install
```

Update your `.env` file with values for each environment variable

```
API_KEY=AIzaSyBkkFF0XhNZeWuDmOfEhsgdfX1VBG7WTas
etc ...
```

Install the Vercel CLI

```
npm install -g vercel
```

Link your codebase to a Vercel project

```
vercel
```

Run the development server

```
vercel dev
```

When the above command completes you'll be able to view your website at `http://localhost:3000`.

_Note: You can run just the front-end with `npm run start`, but `vercel dev` also handles running your API endpoints (located in the `/api` directory)._

## 🥞 Stack

This project uses the following libraries and services:

- Framework - [Create React App](https://create-react-app.dev) with React Router
- UI Kit - [Bootstrap](https://react-bootstrap.github.io)
- Authentication - [Firebase Auth](https://firebase.google.com/products/auth)
- Database - [Cloud Firestore](https://firebase.google.com/products/firestore)
- Payments - [Stripe](https://stripe.com)
- Newsletter - [Mailchimp](https://mailchimp.com)
- Contact Form - [Formspree](https://formspree.io)
- Analytics - [Google Analytics](https://googleanalytics.com)
- Hosting - [Vercel](https://vercel.com)

## 📚 Guide

<details>
<summary><b>Styles</b></summary>
<p>
You can edit Bootstrap SASS variables in the global stylesheet located at <code><a href="src/styles/global.scss">src/styles/global.scss</a></code>. Variables allow you to control global styles (like colors and fonts), as well as element specific styles (like button padding). Before overriding Bootstrap elements with custom style check the <a href="https://getbootstrap.com/docs/4.3/getting-started/introduction/">Bootstrap docs</a> to see if you can do what need by tweaking a SASS variable.
</p>
<p>
Custom styles are located in their related component's directory. For example, if any custom style is applied to the Navbar component you'll find it in <code>src/components/Navbar.scss</code>. We ensure custom styles are scoped to their component by prepending the classname with the component name (such as <code>.Navbar__brand</code>). This ensures styles never affect elements in other components. If styles need to be re-used in multiple components consider creating a new component that encapsulates that style and structure and using that component in multiple places.
</p>
</details>

<details>
<summary><b>Routing</b></summary>
<p>
This project uses <a target="_blank" href="https://reacttraining.com/react-router/web/guides/quick-start">React Router</a> and includes a convenient <code>useRouter</code> hook (located in <code><a href="src/util/router.js">src/util/router.js</a></code>) that wraps React Router and gives all the route methods and data you need.

```jsx
import { Link, useRouter } from './../util/router.js';

function MyComponent(){
// Get the router object
const router = useRouter();

// Get value from query string (?postId=123) or route param (/:postId)
console.log(router.query.postId);

// Get current pathname
console.log(router.pathname)

// Navigate with the <Link> component or with router.push()
return (
<div>
<Link to="/about">About</Link>
<button onClick={(e) => router.push('/about')}>About</button>
</div>
);
}
```
</p>

</details>

<details>
<summary><b>Authentication</b></summary>
<p>
This project uses <a href="https://firebase.google.com">Firebase Auth</a> and includes a convenient <code>useAuth</code> hook (located in <code><a href="src/util/auth.js">src/util/auth.js</a></code>) that wraps Firebase and gives you common authentication methods. Depending on your needs you may want to edit this file and expose more Firebase functionality.

```js
import { useAuth } from './../util/auth.js';

function MyComponent(){
// Get the auth object in any component
const auth = useAuth();

// Depending on auth state show signin or signout button
// auth.user will either be an object, null when loading, or false if signed out
return (
<div>
{auth.user ? (
<button onClick={(e) => auth.signout()}>Signout</button>
) : (
<button onClick={(e) => auth.signin('[email protected]', 'yolo')}>Signin</button>
)}
</div>
);
}
```

</p>
</details>

<details>
<summary><b>Database</b></summary>

<p>
This project uses <a href="https://firebase.google.com/products/firestore">Cloud Firestore</a> and includes some data fetching hooks to get you started (located in <code><a href="src/util/db.js">src/util/db.js</a></code>). You'll want to edit that file and add any additional query hooks you need for your project.

```js
import { useAuth } from './../util/auth.js';
import { useItemsByOwner } from './../util/db.js';
import ItemsList from './ItemsList.js';

function ItemsPage(){
const auth = useAuth();

// Fetch items by owner
// It's okay if uid is undefined while auth is still loading
// The hook will return a "loading" status until it has a uid
const uid = auth.user ? auth.user.uid : undefined;
const { data: items, status } = useItemsByOwner(uid);

// Once we items data then render ItemsList component
return (
<div>
{status === "loading" ? (
<span>One moment please</span>
) : (
<ItemsList data={items}>
)}
</div>
);
}
```

</p>
</details>

<details>
<summary><b>Deployment</b></summary>
<p>
Install the Vercel CLI

```
npm install -g vercel
```

Add each variable from `.env` to your Vercel project with the following command. You'll be prompted to enter its value and then choose one or more environments (development, preview, or production).
<a target="_blank" href="https://vercel.com/docs/v2/build-step#environment-variables">Learn more here</a>.

```
vercel env add VARIABLE_NAME
```

Run this command to deploy a preview (for testing a live deployment)

```
vercel
```

Run this command to deploy to production

```
vercel --prod
```

See the <a target="_blank" href="https://vercel.com/docs/v2/platform/deployments">Vercel docs</a> for more details.
</p>

</details>

<details>
<summary><b>Other</b></summary>
<p>
The <a href="https://create-react-app.dev">Create React App documention</a> covers many other topics.
This project was initially created using <a href="https://divjoy.com?ref=readme_other">Divjoy</a>, a React codebase generator. Feel free to ask questions in the <a href="https://spectrum.chat/divjoy">Divjoy forum</a> and we'll do our best to help you out.
</p>
</details>
53 changes: 53 additions & 0 deletions api/_db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const firebaseAdmin = require("./_firebase");

const firestore = firebaseAdmin.firestore();

// Update an existing user
function updateUser(uid, data) {
return firestore.collection("users").doc(uid).update(data);
}

// Get user by uid
function getUser(uid) {
return firestore.collection("users").doc(uid).get().then(format);
}

// Get user by stripeCustomerId
function getUserByCustomerId(customerId) {
return firestore
.collection("users")
.where("stripeCustomerId", "==", customerId)
.get()
.then(format)
.then((docs) => (docs ? docs[0] : null)); // Get first result
}

// Update a user by their stripeCustomerId
function updateUserByCustomerId(customerId, data) {
return getUserByCustomerId(customerId).then((user) => {
return updateUser(user.id, data);
});
}

/**** HELPERS ****/

// Format Firestore response (handles a collection or single doc)
function format(response) {
if (response.docs) {
return response.docs.map(getDoc);
} else {
return getDoc(response);
}
}

// Get doc data and merge in doc.id
function getDoc(doc) {
return doc.exists ? { id: doc.id, ...doc.data() } : null;
}

module.exports = {
updateUser,
getUser,
getUserByCustomerId,
updateUserByCustomerId,
};
14 changes: 14 additions & 0 deletions api/_firebase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const firebaseAdmin = require("firebase-admin");

if (!firebaseAdmin.apps.length) {
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"),
}),
databaseURL: `https://${process.env.FIREBASE_PROJECT_ID}.firebaseio.com`,
});
}

module.exports = firebaseAdmin;
33 changes: 33 additions & 0 deletions api/_require-auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const firebaseAdmin = require("./_firebase.js");

// Middleware for requiring authentication and getting user
const requireAuth = (fn) => async (req, res) => {
// Respond with error if no authorization header
if (!req.headers.authorization) {
return res.status(401).send({
status: "error",
message: "You must be signed in to call this endpoint",
});
}

// Get access token from authorization header ("Bearer: xxxxxxx")
const accessToken = req.headers.authorization.split(" ")[1];

try {
// Get user from token and add to req object
req.user = await firebaseAdmin.auth().verifyIdToken(accessToken);

// Call route function passed into this middleware
return fn(req, res);
} catch (error) {
// If there's an error assume token is expired and return
// auth/invalid-user-token error (handled by apiRequest in util.js)
res.status(401).send({
status: "error",
code: "auth/invalid-user-token",
message: "Your login has expired. Please login again.",
});
}
};

module.exports = requireAuth;
Loading

0 comments on commit 7bd6f04

Please sign in to comment.