forked from wesbos/master-gatsby
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
1,338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import Layout from './src/components/Layout'; | ||
import { OrderProvider } from './src/components/OrderContext'; | ||
|
||
export function wrapPageElement({ element, props }) { | ||
return <Layout {...props}>{element}</Layout>; | ||
} | ||
|
||
export function wrapRootElement({ element }) { | ||
return <OrderProvider>{element}</OrderProvider>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import path, { resolve } from 'path'; | ||
import fetch from 'isomorphic-fetch'; | ||
|
||
async function turnPizzasIntoPages({ graphql, actions }) { | ||
// 1. Get a template for this page | ||
const pizzaTemplate = path.resolve('./src/templates/Pizza.js'); | ||
// 2. Query all pizzas | ||
const { data } = await graphql(` | ||
query { | ||
pizzas: allSanityPizza { | ||
nodes { | ||
name | ||
slug { | ||
current | ||
} | ||
} | ||
} | ||
} | ||
`); | ||
// 3. Loop over each pizza and create a page for that pizza | ||
data.pizzas.nodes.forEach((pizza) => { | ||
actions.createPage({ | ||
// What is the URL for this new page?? | ||
path: `pizza/${pizza.slug.current}`, | ||
component: pizzaTemplate, | ||
context: { | ||
slug: pizza.slug.current, | ||
}, | ||
}); | ||
}); | ||
} | ||
|
||
async function turnToppingsIntoPages({ graphql, actions }) { | ||
// 1. Get the template | ||
const toppingTemplate = path.resolve('./src/pages/pizzas.js'); | ||
// 2. query all the toppings | ||
const { data } = await graphql(` | ||
query { | ||
toppings: allSanityTopping { | ||
nodes { | ||
name | ||
id | ||
} | ||
} | ||
} | ||
`); | ||
// 3. createPage for that topping | ||
data.toppings.nodes.forEach((topping) => { | ||
actions.createPage({ | ||
path: `topping/${topping.name}`, | ||
component: toppingTemplate, | ||
context: { | ||
topping: topping.name, | ||
// TODO Regex for Topping | ||
toppingRegex: `/${topping.name}/i`, | ||
}, | ||
}); | ||
}); | ||
// 4. Pass topping data to pizza.js | ||
} | ||
|
||
async function fetchBeersAndTurnIntoNodes({ | ||
actions, | ||
createNodeId, | ||
createContentDigest, | ||
}) { | ||
// 1. Fetch a list of beers | ||
const res = await fetch('https://sampleapis.com/beers/api/ale'); | ||
const beers = await res.json(); | ||
// 2. Loop over each one | ||
for (const beer of beers) { | ||
// create a node for each beer | ||
const nodeMeta = { | ||
id: createNodeId(`beer-${beer.name}`), | ||
parent: null, | ||
children: [], | ||
internal: { | ||
type: 'Beer', | ||
mediaType: 'application/json', | ||
contentDigest: createContentDigest(beer), | ||
}, | ||
}; | ||
// 3. Create a node for that beer | ||
actions.createNode({ | ||
...beer, | ||
...nodeMeta, | ||
}); | ||
} | ||
} | ||
|
||
async function turnSlicemastersIntoPages({ graphql, actions }) { | ||
// 1. Query all slicemasters | ||
const { data } = await graphql(` | ||
query { | ||
slicemasters: allSanityPerson { | ||
totalCount | ||
nodes { | ||
name | ||
id | ||
slug { | ||
current | ||
} | ||
} | ||
} | ||
} | ||
`); | ||
// TODO: 2. Turn each slicemaster into their own page (TODO) | ||
data.slicemasters.nodes.forEach((slicemaster) => { | ||
actions.createPage({ | ||
component: resolve('./src/templates/Slicemaster.js'), | ||
path: `/slicemaster/${slicemaster.slug.current}`, | ||
context: { | ||
name: slicemaster.person, | ||
slug: slicemaster.slug.current, | ||
}, | ||
}); | ||
}); | ||
|
||
// 3. Figure out how many pages there are based on how many slicemasters there are, and how many per page! | ||
const pageSize = parseInt(process.env.GATSBY_PAGE_SIZE); | ||
const pageCount = Math.ceil(data.slicemasters.totalCount / pageSize); | ||
console.log( | ||
`There are ${data.slicemasters.totalCount} total people. And we have ${pageCount} pages with ${pageSize} per page` | ||
); | ||
// 4. Loop from 1 to n and create the pages for them | ||
Array.from({ length: pageCount }).forEach((_, i) => { | ||
console.log(`Creating page ${i}`); | ||
actions.createPage({ | ||
path: `/slicemasters/${i + 1}`, | ||
component: path.resolve('./src/pages/slicemasters.js'), | ||
// This data is pass to the template when we create it | ||
context: { | ||
skip: i * pageSize, | ||
currentPage: i + 1, | ||
pageSize, | ||
}, | ||
}); | ||
}); | ||
} | ||
|
||
export async function sourceNodes(params) { | ||
// fetch a list of beers and source them into our gatsby API! | ||
await Promise.all([fetchBeersAndTurnIntoNodes(params)]); | ||
} | ||
|
||
export async function createPages(params) { | ||
// Create pages dynamically | ||
// Wait for all promises to be resolved before finishing this function | ||
await Promise.all([ | ||
turnPizzasIntoPages(params), | ||
turnToppingsIntoPages(params), | ||
turnSlicemastersIntoPages(params), | ||
]); | ||
// 1. Pizzas | ||
// 2. Toppings | ||
// 3. Slicemasters | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
exports.handler = async (event, context) => { | ||
console.log(event); | ||
return { | ||
statusCode: 200, | ||
body: 'Hello!!', | ||
}; | ||
}; |
13 changes: 13 additions & 0 deletions
13
stepped-solutions/39/functions/placeOrder/package-lock.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "placeorder", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "placeOrder.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"nodemailer": "^6.4.10" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const nodemailer = require('nodemailer'); | ||
|
||
// create a transport for nodemailer | ||
const transporter = nodemailer.createTransport({ | ||
host: process.env.MAIL_HOST, | ||
port: 587, | ||
auth: { | ||
user: process.env.MAIL_USER, | ||
pass: process.env.MAIL_PASS, | ||
}, | ||
}); | ||
|
||
exports.handler = async (event, context) => { | ||
// Test send an email | ||
const info = await transporter.sendMail({ | ||
from: "Slick's Slices <[email protected]>", | ||
to: '[email protected]', | ||
subject: 'New order!', | ||
html: `<p>Your new pizza order is here!</p>`, | ||
}); | ||
console.log(info); | ||
return { | ||
statusCode: 200, | ||
body: JSON.stringify(info), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[build] | ||
functions = "functions/" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import formatMoney from './formatMoney'; | ||
import calculatePizzaPrice from './calcuatePizzaPrice'; | ||
|
||
export default function attachNamesAndPrices(order, pizzas) { | ||
return order.map((item) => { | ||
const pizza = pizzas.find((pizza) => pizza.id === item.id); | ||
return { | ||
...item, | ||
name: pizza.name, | ||
thumbnail: pizza.image.asset.fluid.src, | ||
price: formatMoney(calculatePizzaPrice(pizza.price, item.size)), | ||
}; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import React, { useState } from 'react'; | ||
import { graphql } from 'gatsby'; | ||
import Img from 'gatsby-image'; | ||
import SEO from '../components/SEO'; | ||
import useForm from '../utils/useForm'; | ||
import calculatePizzaPrice from '../utils/calcuatePizzaPrice'; | ||
import formatMoney from '../utils/formatMoney'; | ||
import OrderStyles from '../styles/OrderStyles'; | ||
import MenuItemStyles from '../styles/MenuItemStyles'; | ||
import usePizza from '../utils/usePizza'; | ||
import PizzaOrder from '../components/PizzaOrder'; | ||
import calculateOrderTotal from '../utils/calculateOrderTotal'; | ||
|
||
export default function OrderPage({ data }) { | ||
const pizzas = data.pizzas.nodes; | ||
const { values, updateValue } = useForm({ | ||
name: '', | ||
email: '', | ||
}); | ||
const { | ||
order, | ||
addToOrder, | ||
removeFromOrder, | ||
error, | ||
loading, | ||
message, | ||
submitOrder, | ||
} = usePizza({ | ||
pizzas, | ||
values, | ||
}); | ||
|
||
if (message) { | ||
return <p>{message}</p>; | ||
} | ||
return ( | ||
<> | ||
<SEO title="Order a Pizza!" /> | ||
<OrderStyles onSubmit={submitOrder}> | ||
<fieldset> | ||
<legend>Your Info</legend> | ||
<label htmlFor="name">Name</label> | ||
<input | ||
type="text" | ||
name="name" | ||
id="name" | ||
value={values.name} | ||
onChange={updateValue} | ||
/> | ||
<label htmlFor="email">Email</label> | ||
<input | ||
type="email" | ||
name="email" | ||
id="email" | ||
value={values.email} | ||
onChange={updateValue} | ||
/> | ||
</fieldset> | ||
<fieldset className="menu"> | ||
<legend>Menu</legend> | ||
{pizzas.map((pizza) => ( | ||
<MenuItemStyles key={pizza.id}> | ||
<Img | ||
width="50" | ||
height="50" | ||
fluid={pizza.image.asset.fluid} | ||
alt={pizza.name} | ||
/> | ||
<div> | ||
<h2>{pizza.name}</h2> | ||
</div> | ||
<div> | ||
{['S', 'M', 'L'].map((size) => ( | ||
<button | ||
type="button" | ||
key={size} | ||
onClick={() => | ||
addToOrder({ | ||
id: pizza.id, | ||
size, | ||
}) | ||
} | ||
> | ||
{size} {formatMoney(calculatePizzaPrice(pizza.price, size))} | ||
</button> | ||
))} | ||
</div> | ||
</MenuItemStyles> | ||
))} | ||
</fieldset> | ||
<fieldset className="order"> | ||
<legend>Order</legend> | ||
<PizzaOrder | ||
order={order} | ||
removeFromOrder={removeFromOrder} | ||
pizzas={pizzas} | ||
/> | ||
</fieldset> | ||
<fieldset> | ||
<h3> | ||
Your Total is {formatMoney(calculateOrderTotal(order, pizzas))} | ||
</h3> | ||
<div>{error ? <p>Error: {error}</p> : ''}</div> | ||
<button type="submit" disabled={loading}> | ||
{loading ? 'Placing Order...' : 'Order Ahead'} | ||
</button> | ||
</fieldset> | ||
</OrderStyles> | ||
</> | ||
); | ||
} | ||
|
||
export const query = graphql` | ||
query { | ||
pizzas: allSanityPizza { | ||
nodes { | ||
name | ||
id | ||
slug { | ||
current | ||
} | ||
price | ||
image { | ||
asset { | ||
fluid(maxWidth: 100) { | ||
...GatsbySanityImageFluid | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`; |
Oops, something went wrong.