Compare commits
2 Commits
master
...
cart-serve
Author | SHA1 | Date |
---|---|---|
Keith Irwin | 3d0a2447ac | |
Keith Irwin | 537e7c9b9c |
|
@ -1,11 +1,9 @@
|
|||
# Dev
|
||||
SITE_DOMAIN="http://localhost:8080"
|
||||
API_DOMAIN="http://localhost:8081"
|
||||
DOMAIN="http://localhost:8080"
|
||||
STRIPE_SEC="sk_test_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
STRIPE_PUB="pk_test_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
|
||||
# Production
|
||||
#SITE_DOMAIN="https://www.example.com"
|
||||
#API_DOMAIN="https://api.example.com"
|
||||
#DOMAIN="https://www.example.com"
|
||||
#STRIPE_PUB="pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
#STRIPE_SEC="sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
# node.js installs
|
||||
# Packages
|
||||
**/node_modules/
|
||||
**/package-lock.json
|
||||
|
||||
# environment
|
||||
# Environment
|
||||
.env
|
||||
docker-compose.yml
|
||||
data/
|
||||
_src/_data/
|
||||
|
||||
# frontend build
|
||||
site/_site/
|
||||
# Build
|
||||
_site/
|
||||
_functions/dynamic/**
|
||||
!_functions/dynamic/index.js
|
||||
|
||||
# aspell backups
|
||||
# Backups and tempfiles
|
||||
**/*.bak
|
||||
# vim swaps
|
||||
**/*.swp
|
||||
|
||||
# https://www.11ty.dev/docs/plugins/serverless/
|
||||
site/functions/serverless/**
|
||||
!site/functions/serverless/index.js
|
78
README.md
|
@ -1,9 +1,77 @@
|
|||
# eleventy-base-blog
|
||||
|
||||
# Shopity
|
||||
A starter repository showing how to build a blog with the [Eleventy](https://github.com/11ty/eleventy) static site generator.
|
||||
|
||||
Ecommerce template using 11ty, knockout, stripe, docker
|
||||
[![Build Status](https://travis-ci.org/11ty/eleventy-base-blog.svg?branch=master)](https://travis-ci.org/11ty/eleventy-base-blog)
|
||||
|
||||
## Setting shipping rates data
|
||||
## Demos
|
||||
|
||||
- **zips-to-zones:** [USPS Zone chart by zip code](https://postcalc.usps.com/DomesticZoneChart)
|
||||
- **usps-shipping-rates:** [USPS Rates by zone/weight](https://pe.usps.com/text/dmm300/Notice123.htm#_c078)
|
||||
* [Netlify](https://eleventy-base-blog.netlify.com/)
|
||||
* [GitHub Pages](https://11ty.github.io/eleventy-base-blog/)
|
||||
* [Remix on Glitch](https://glitch.com/~11ty-eleventy-base-blog)
|
||||
|
||||
## Deploy this to your own site
|
||||
|
||||
These builders are amazing—try them out to get your own Eleventy site in a few clicks!
|
||||
|
||||
* [Get your own Eleventy web site on Netlify](https://app.netlify.com/start/deploy?repository=https://github.com/11ty/eleventy-base-blog)
|
||||
* [Get your own Eleventy web site on Vercel](https://vercel.com/import/project?template=11ty%2Feleventy-base-blog)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Clone this Repository
|
||||
|
||||
```
|
||||
git clone https://github.com/11ty/eleventy-base-blog.git my-blog-name
|
||||
```
|
||||
|
||||
|
||||
### 2. Navigate to the directory
|
||||
|
||||
```
|
||||
cd my-blog-name
|
||||
```
|
||||
|
||||
Specifically have a look at `.eleventy.js` to see if you want to configure any Eleventy options differently.
|
||||
|
||||
### 3. Install dependencies
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### 4. Edit _data/metadata.json
|
||||
|
||||
### 5. Run Eleventy
|
||||
|
||||
```
|
||||
npx eleventy
|
||||
```
|
||||
|
||||
Or build and host locally for local development
|
||||
```
|
||||
npx eleventy --serve
|
||||
```
|
||||
|
||||
Or build automatically when a template changes:
|
||||
```
|
||||
npx eleventy --watch
|
||||
```
|
||||
|
||||
Or in debug mode:
|
||||
```
|
||||
DEBUG=* npx eleventy
|
||||
```
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
* `about/index.md` shows how to add a content page.
|
||||
* `posts/` has the blog posts but really they can live in any directory. They need only the `post` tag to be added to this collection.
|
||||
* Add the `nav` tag to add a template to the top level site navigation. For example, this is in use on `index.njk` and `about/index.md`.
|
||||
* Content can be any template format (blog posts needn’t be markdown, for example). Configure your supported templates in `.eleventy.js` -> `templateFormats`.
|
||||
* Because `css` and `png` are listed in `templateFormats` but are not supported template types, any files with these extensions will be copied without modification to the output (while keeping the same directory structure).
|
||||
* The blog post feed template is in `feed/feed.njk`. This is also a good example of using a global data files in that it uses `_data/metadata.json`.
|
||||
* This example uses three layouts:
|
||||
* `_includes/layouts/base.njk`: the top level HTML structure
|
||||
* `_includes/layouts/post.njk`: the blog post template (wrapped into `base.njk`)
|
||||
* `_includes/postlist.njk` is a Nunjucks include and is a reusable component used to display a list of all the posts. `index.njk` has an example of how to use it.
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
const { EleventyServerless } = require("@11ty/eleventy");
|
||||
|
||||
// Explicit dependencies for the bundler from config file and global data.
|
||||
// The file is generated by the Eleventy Serverless Bundler Plugin.
|
||||
require("./eleventy-bundler-modules.js");
|
||||
|
||||
async function handler(event) {
|
||||
let elev = new EleventyServerless("dynamic", {
|
||||
path: new URL(event.rawUrl).pathname,
|
||||
query: event.multiValueQueryStringParameters || event.queryStringParameters,
|
||||
functionsDir: "_functions",
|
||||
});
|
||||
|
||||
try {
|
||||
let [page] = await elev.getOutput();
|
||||
|
||||
// If you want some of the data cascade available in `page.data`, use `eleventyConfig.dataFilterSelectors`.
|
||||
// Read more: https://www.11ty.dev/docs/config/#data-filter-selectors
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
"Content-Type": "text/html; charset=UTF-8",
|
||||
},
|
||||
body: page.content,
|
||||
};
|
||||
} catch (error) {
|
||||
// Only console log for matching serverless paths
|
||||
// (otherwise you’ll see a bunch of BrowserSync 404s for non-dynamic URLs during --serve)
|
||||
if (elev.isServerlessUrl(event.path)) {
|
||||
console.log("Serverless Error:", error);
|
||||
}
|
||||
|
||||
return {
|
||||
statusCode: error.httpStatusCode || 500,
|
||||
body: JSON.stringify(
|
||||
{
|
||||
error: error.message,
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Choose one:
|
||||
// * Runs on each request: AWS Lambda, Netlify Function
|
||||
// * Runs on first request only: Netlify On-demand Builder
|
||||
// 1. Don’t forget to `npm install @netlify/functions`
|
||||
// 2. Also use `redirects: "netlify-toml-builders"` in your config file’s serverless bundler options:
|
||||
// https://www.11ty.dev/docs/plugins/serverless/#bundler-options
|
||||
|
||||
exports.handler = handler;
|
||||
|
||||
//const { builder } = require("@netlify/functions");
|
||||
//exports.handler = builder(handler);
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
permalink: /assets/data/products.json
|
||||
---
|
||||
{
|
||||
{%- for prod in collections.products %}
|
||||
"{{prod.data.pid}}/{{prod.data.sid}}": {
|
||||
"price": {{prod.data.price|dump|safe}},
|
||||
"weight": {{prod.data.specs.weight|dump|safe}},
|
||||
"volume": {{prod.data.specs.dimensions|dimsToVol|dump|safe}}
|
||||
}{% if loop.index!==loop.length %},{% endif %}
|
||||
{%- endfor %}
|
||||
}
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 495 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 202 KiB |
Before Width: | Height: | Size: 879 B After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 507 B |
|
@ -192,11 +192,11 @@ class Cart { constructor() {
|
|||
)
|
||||
self.checkoutButtonText = ko.pureComputed(() => {
|
||||
if (self.isLoading())
|
||||
return '📃 Creating order...'
|
||||
return 'Checking out...'
|
||||
else if (self.subtotal()>0)
|
||||
return `📃 Create order for ${self.total_pretty()}`
|
||||
return `Checkout for ${self.total_pretty()}`
|
||||
else
|
||||
return '📃 Create order'
|
||||
return 'Checkout'
|
||||
})
|
||||
self.checkoutButtonHelp = ko.pureComputed(() => {
|
||||
if (self.total()===0) return 'Add items to your cart first'
|
||||
|
@ -247,17 +247,17 @@ class Cart { constructor() {
|
|||
|
||||
if (self.payment_method()==='USD') {
|
||||
// Send to server
|
||||
const res = await fetch(`${API_DOMAIN}/order`, {
|
||||
method: 'POST', mode: 'cors',
|
||||
headers: {'Content-Type':'application/json'},
|
||||
body: ko.toJSON(order),
|
||||
})
|
||||
localStorage.setItem('order_data', await res.json())
|
||||
//const res = await fetch('/shop/checkout/order', {
|
||||
// method: 'POST', mode: 'cors',
|
||||
// headers: {'Content-Type':'application/json'},
|
||||
// body: ko.toJSON(order),
|
||||
//})
|
||||
//localStorage.setItem('order_data', await res.json())
|
||||
// Send user to checkout
|
||||
window.location = '/shop/checkout/stripe/'
|
||||
} else self.isLoading(false)
|
||||
}
|
||||
|
||||
|
||||
// On first load, read from localStorage
|
||||
Object.keys(localStorage).forEach(i => {
|
||||
if (i.substring(0,5)==='cart_') {
|
|
@ -1,6 +1,5 @@
|
|||
const formatUSD = (v) => v.toLocaleString(undefined, {
|
||||
style: 'currency', currency: 'USD' })
|
||||
const order = JSON.parse(localStorage.getItem('order_data'))
|
||||
const cancel = () => {
|
||||
localStorage.removeItem('order_data')
|
||||
window.location = '/shop/cart/'
|
||||
|
@ -18,8 +17,8 @@ const elements = stripe.elements({
|
|||
fontFamily: 'dejavu, monospace',
|
||||
},
|
||||
},
|
||||
clientSecret: order.stripe_secret,
|
||||
})
|
||||
clientSecret: JSON.parse(localStorage.getItem('order_data')).stripe_secret,
|
||||
})
|
||||
const paymentElement = elements.create('payment', {
|
||||
paymentMethodOrder: ['card'],
|
||||
layout: 'tabs', // or 'accordian'
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
@ -86,14 +86,13 @@ title: Cart
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button style="margin:calc(2vh + 1vw) 0" data-bind="visible:(payment_method()==='USD'), click:checkout,enable:(checkoutEnabled()&&!isLoading()), attr:{title:checkoutButtonHelp()}, text:checkoutButtonText">📃 Create order</button>
|
||||
<button style="margin:calc(2vh + 1vw) 0" data-bind="visible:(payment_method()==='USD'), click:checkout,enable:(checkoutEnabled()&&!isLoading()), attr:{title:checkoutButtonHelp()}, text:checkoutButtonText">Checkout</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_DOMAIN = '{{env.API_DOMAIN}}'
|
||||
const ZIPS_TO_STATES = '{{zipsToStates|dump|safe}}'
|
||||
const ZIPS_TO_ZONES = '{{zipsToZones|dump|safe}}'
|
||||
const USPS_SHIPPING_RATES = '{{uspsShippingRates|dump|safe}}'
|
||||
const ZIPS_TO_STATES = JSON.parse('{{zipsToStates|dump|safe}}')
|
||||
const ZIPS_TO_ZONES = JSON.parse('{{zipsToZones|dump|safe}}')
|
||||
const USPS_SHIPPING_RATES = JSON.parse('{{uspsShippingRates|dump|safe}}')
|
||||
</script>
|
||||
<script src="/assets/scripts/knockout-3.5.1.min.js" integrity="sha512-2AL/VEauKkZqQU9BHgnv48OhXcJPx9vdzxN1JrKDVc4FPU/MEE/BZ6d9l0mP7VmvLsjtYwqiYQpDskK9dG8KBA=="></script>
|
||||
<script src="/assets/scripts/cart.js" defer></script>
|
|
@ -1,10 +1,14 @@
|
|||
---
|
||||
layout: layouts/base.njk
|
||||
title: Checkout
|
||||
permalink:
|
||||
dynamic: /shop/checkout/stripe/
|
||||
---
|
||||
|
||||
<h1>📃 Order</h1>
|
||||
<h1>Checkout</h1>
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<p>page: {{page|dump}}</p>
|
||||
<p>eleventy: {{eleventy|dump}}</p>
|
||||
<table style="width:100%;text-align:left"><thead>
|
||||
<th>Item</th>
|
||||
<th style="text-align:right">Price</th>
|