Skip to content

Commit

Permalink
stepped
Browse files Browse the repository at this point in the history
  • Loading branch information
wesbos committed Aug 13, 2020
1 parent 213de4d commit c730a5a
Show file tree
Hide file tree
Showing 23 changed files with 1,338 additions and 0 deletions.
11 changes: 11 additions & 0 deletions stepped-solutions/38/gatsby-browser.js
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>;
}
157 changes: 157 additions & 0 deletions stepped-solutions/38/gatsby-node.js
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
}
7 changes: 7 additions & 0 deletions stepped-solutions/39/functions/hello/hello.js
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 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.

14 changes: 14 additions & 0 deletions stepped-solutions/39/functions/placeOrder/package.json
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"
}
}
26 changes: 26 additions & 0 deletions stepped-solutions/39/functions/placeOrder/placeOrder.js
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),
};
};
2 changes: 2 additions & 0 deletions stepped-solutions/39/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
functions = "functions/"
14 changes: 14 additions & 0 deletions stepped-solutions/40/attachNamesAndPrices.js
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)),
};
});
}
133 changes: 133 additions & 0 deletions stepped-solutions/40/order.js
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
}
}
}
}
}
}
`;
Loading

0 comments on commit c730a5a

Please sign in to comment.