Skip to content
This repository was archived by the owner on Apr 12, 2023. It is now read-only.

Major codebase update #17

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
df52b1b
Add Sanity project id
mornir Jan 31, 2021
f3aceb6
Add jsconfig
mornir Jan 31, 2021
500bcf5
Update all deps
mornir Jan 31, 2021
14f7883
Remove postcss
mornir Jan 31, 2021
6c6d3bb
Use nuxt server init
mornir Jan 31, 2021
d33612b
Fix eslint config
mornir Jan 31, 2021
e70d6c8
Add target static
mornir Jan 31, 2021
4745c86
Fix nuxtServerInit
mornir Jan 31, 2021
eac8430
Add nuxt-sanity module
mornir Jan 31, 2021
2791223
Use SanityContent and SanityImage
mornir Jan 31, 2021
bde88de
Add nuxt-snipcart module
mornir Jan 31, 2021
6c3db55
Remove unneeded ref
mornir Jan 31, 2021
b316482
Yarn upgrade
mornir Jan 31, 2021
9f880fd
Temp disable routes generation
mornir Jan 31, 2021
d96a16a
Generate pages at build time
mornir Feb 1, 2021
a2cf47c
Run yarn lint
mornir Feb 2, 2021
0eaff82
Lint readme
mornir Feb 2, 2021
0865aef
Update readme
mornir Feb 2, 2021
da2e251
Remove numeral, intl.numberformat instead
mornir Feb 2, 2021
1fe24e7
Update readme
mornir Feb 2, 2021
f1f2902
Code review
mornir Feb 2, 2021
67a49f9
Remove unused component
mornir Feb 2, 2021
62fdee4
Fix list styling
mornir Feb 2, 2021
dc4c19f
Update readme
mornir Feb 2, 2021
03cd73b
Add more information to readme
mornir Feb 2, 2021
fdfa12d
Remove keys
mornir Feb 2, 2021
2e14953
Remove keys (+24 squashed commit)
mornir Jan 31, 2021
0c9a99d
Merge branches '2021-refresh' and '2021-refresh' of https://github.co…
mornir Feb 2, 2021
ccf6f45
Add back original project id and snipcart key
mornir Feb 2, 2021
64dcb22
Force Netlify to use Node v14
mornir Feb 2, 2021
3d64919
Update Netlify build commands
mornir Feb 2, 2021
a2a568b
Provide modern build for evergreen browsers
mornir Feb 2, 2021
8888756
Remove uncessary import
mornir Feb 3, 2021
ca3f972
Fix snipcart data-item-url
mornir Feb 3, 2021
183a8b9
Use @sanity/image-url instead of SanityImage
mornir Feb 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@ module.exports = {
root: true,
env: {
browser: true,
node: true
node: true,
},
parserOptions: {
parser: 'babel-eslint'
parser: 'babel-eslint',
},
extends: [
'eslint:recommended',
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/recommended',
'plugin:prettier/recommended'
'@nuxtjs',
'prettier',
'prettier/vue',
'plugin:prettier/recommended',
'plugin:nuxt/recommended',
],
// required to lint *.vue files
plugins: ['vue', 'prettier'],
plugins: ['prettier'],
// add your custom rules here
rules: {
semi: [2, 'never'],
'vue/max-attributes-per-line': 'off',
'prettier/prettier': 'error'
}
};
'no-console': 'off',
},
}
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
14
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"semi": false
"semi": false,
"singleQuote": true
}
106 changes: 80 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,115 @@
# The Transglobal Candy Store

A frontend example in Vue.js and Nuxt.js for the Sanity.io e-commerce schema
A frontend example in [Vue.js](https://vuejs.org/) and [Nuxt.js](https://nuxtjs.org) for the Sanity.io e-commerce schema

🔗 [Read the blog post](https://www.sanity.io/blog/e-commerce-vue-nuxt-snipcart)

![Short animated preview of the app](https://public.sanity.io/github-assets/snipcart-for-github.gif "Logo Title Text 1")

![Short animated preview of the app](https://public.sanity.io/github-assets/snipcart-for-github.gif 'Short animated preview of the app')

## Quickstart on local

``` bash
```bash
# install dependencies
$ npm install
$ yarn install

# serve with hot reload at localhost:3000
$ npm run dev
$ yarn dev
```

Tips:

- Make sure you are running on http://localhost:3000. If not sanity and snipcart will fail due to CORS origins.
- Vue.js requires a recent Node version so if it fails on startup you might need an upgrade.
- This project requires a recent Node version 12 or later, so if it fails on startup you might need an upgrade.

## Using your own sanity.io data
## Using your own backend data (Sanity Studio)

You're about five minutes away from running this example with your own data. You'll need to set up a Sanity studio for this so:
You're about five minutes away from running this example with your own data. You'll need to set up a Sanity Studio for this so:

- If you don't have Sanity CLI version 0.130.0 or later, install or upgrade it with `npm install -g @sanity/cli`
- If you don't have Sanity CLI version 2.3.2 or later, install it with `yarn global add @sanity/cli` or upgrade it with `yarn global upgrade @sanity/cli`
- Initialize a new project with `sanity init` and select the e-commerce schema
- `sanity start` will start your studio and let you start adding your products!
- Go to `sanity.json` and locate your `projectId` and `dataset`

Head back to this project and in `sanity.js` change the `projectId` and `dataset` values to the ones you found above
Head back to this project and in `nuxt.config.js` scroll down and change the `projectId` and `dataset` values to the ones you found above.

Tips:
- Remember to add CORS manage.sanity.io (ex. http://localhost:3000 to run locally)
- You can `sanity deploy` your editor to share it with others

- Remember to allow CORS for your project URLs in manage.sanity.io (ex. http://localhost:3000 to run locally)
- You can `sanity deploy` the Studio to share it with others

## Install your own snipcart

- Go to http://snipcart.com
- Register and copy your API-key from snipcart
- In `nuxt.config.js` paste it into `data-api-key`
- Register (Snipcart is free in Test mode) and copy your public test API key from your [snipcart dashboard](https://app.snipcart.com/dashboard/account/credentials)
- In `nuxt.config.js` scroll down und past it into the `snipcart` option
- Remember to add your domain/url in your Snicart settings (https://app.snipcart.com/dashboard/account/domains)

## Build production server or static project
``` bash
# build for production and launch server
$ npm run build
$ npm start

# generate static project
$ npm run generate
```bash
# generate static project and test locally
$ yarn generate
$ yarn start
```

## Note on Nuxt crawler

This project is built in the [Nuxt Full Static mode](https://nuxtjs.org/blog/going-full-static/), using the Nuxt crawler to detect every relative link and generat it.
It means that, in production, the website won't make any API call to Sanity. However, it will make an API call for every pages during build time. And those calls aren't made to the CDN because we always want to have the freshest data when content editors trigger a rebuid.

This approach is fine for websites with not too many pages. If the datastore contains a lot of products, then it's better turn off the crawler and fetch all routes with a single API request:

```js
// nuxt.config.js
generate: {
fallback: true,
crawler: false,
async routes() {
const paths = await client.fetch(`{
"product": *[_type == "product"].slug.current,
"category": *[_type == "category"].slug.current,
"vendor": *[_type == "vendor"].slug.current
}`)
return Object.keys(paths).reduce(
(acc, key) => [
...acc,
...paths[key].reduce((acc, curr) => [...acc, `${key}/${curr}`], []),
],
[]
)
},
},
```

Note that this project is using a lightweight version of the `@sanity/client`, which uses the Fetch API and thus only works in the browser. Since the routes generation is done in the Node runtime, we need to either patch the global object in Node or use the `@sanity/client`.

```js
// nuxt.config.js
import { createClient } from '@nuxtjs/sanity'
import fetch from 'node-fetch'
if (!globalThis.fetch) {
globalThis.fetch = fetch
}

const client = createClient({
projectId: 'xxxxxx',
minimal: true,
useCdn: false,
dataset: 'production',
})
```

If you want to host this on Netlify, as a static build, follow [these steps](https://www.sanity.io/blog/tutorial-host-your-sanity-based-next-js-project-on-netlify#3-deploy-your-blog-on-netlify) while switching out the `generate` command above and changing the output directory from `out` to `dist`. Note: Nuxt is intended to run as a universal/isomorphic app and will make calls to the Sanity CDN.
And don't forget to return the payload and thus prevent the API calls in your pages:

```js
asyncData({ $sanity, params, payload }) {
if(payload) return { product: localize(data) }
return $sanity
.fetch(query, params)
.then((data) => ({ product: localize(data) }))
},
```

The queries are by default limited to 100 items. This project is just an example, but
it is possible to expand it with pagination or forever-scroll. To get more items,
just add ex [0..1000] to the end of your query https://www.sanity.io/docs/data-store/query-cheat-sheet#slice-operations
## Note on GROQ query default limit

For detailed explanations on how Nuxt.js work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js).
The queries are by default limited to 100 items. This project is just an example, but it is possible to expand it with pagination or forever-scroll. To get more items, just add ex [0..1000] to the end of your query https://www.sanity.io/docs/data-store/query-cheat-sheet#slice-operations
29 changes: 10 additions & 19 deletions components/ImageViewer.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<template>
<div class="root">
<div style="cursor: pointer" @click="setActiveImage(images[0])">
<SanityImage
:image="images[0]"
:width="mainImageWidth"
<img
:src="$urlFor(images[0]).size(500)"
width="500"
height="500"
class="mainImage"
/>
</div>
Expand All @@ -14,7 +15,7 @@
class="item"
@click="setActiveImage(image)"
>
<SanityImage :image="image" :width="imageWidth" class="image" />
<img :src="$urlFor(image).size(200)" width="200" class="image" />
</li>
</ul>

Expand All @@ -25,39 +26,29 @@
points="96,14 82,0 48,34 14,0 0,14 34,48 0,82 14,96 48,62 82,96 96,82 62,48 "
/>
</svg>
<SanityImage :image="activeImage" class="image" width="1000" />
<img :src="$urlFor(activeImage).size(1000)" class="image" width="1000" />
</div>
</div>
</template>

<script>
import SanityImage from "./SanityImage"

export default {
components: {
SanityImage
},
props: {
images: { type: Array, required: true }
images: { type: Array, required: true },
},
data() {
return {
mainImageWidth: 500,
imageWidth: 200,
activeImage: null
activeImage: null,
}
},
computed: {
mainImage: () => this.images[0]
},
methods: {
setActiveImage(image) {
this.activeImage = image
},
close() {
this.activeImage = null
}
}
},
},
}
</script>

Expand Down
26 changes: 0 additions & 26 deletions components/Price.vue

This file was deleted.

46 changes: 22 additions & 24 deletions components/ProductList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@
</div>
<ul v-if="products.length > 0" :class="displayView">
<li v-for="product in products" :key="product._id" class="product">
<router-link :to="'/product/' + product.slug.current" class="link">
<SanityImage
<NuxtLink :to="'/product/' + product.slug.current" class="link">
<img
v-if="product.defaultProductVariant.images[0]"
:image="product.defaultProductVariant.images[0]"
:src="$urlFor(product.defaultProductVariant.images[0]).size(300)"
:alt="product.title"
:width="displayView === 'grid' ? 300 : 50"
class="image"
loading="lazy"
/>

<div class="title">{{ product.title }}</div>
<h3 class="title">{{ product.title }}</h3>
<p v-if="displayView === 'grid'" class="blurb">{{ product.blurb }}</p>
</router-link>
</NuxtLink>

<div class="price-and-button">
<span class="price">{{
Expand All @@ -47,9 +48,12 @@
:data-item-name="product.title"
:data-item-price="product.defaultProductVariant.price"
:data-item-id="product._id"
:data-item-url="'/product/' + product.slug.current"
:data-item-image="
$urlFor(product.defaultProductVariant.images[0]).size(120)
"
type="button"
class="snipcart-add-item"
data-item-url="/"
>
Add to cart
</button>
Expand All @@ -60,40 +64,34 @@
</template>

<script>
import SanityImage from "~/components/SanityImage"
import lineClamp from "vue-line-clamp"
import numeral from "numeral"

export default {
directives: {
lineClamp
},
components: {
SanityImage
},
props: {
products: {
type: Array,
required: true
required: true,
default: () => [],
},
view: {
type: String,
default: "grid"
}
default: 'grid',
},
},
data(context) {
data() {
return {
displayView: context._props.view || "grid"
displayView: this.view,
}
},
methods: {
setView(view) {
this.displayView = view
},
getFormattedPrice(price) {
return numeral(price).format("$0.00")
}
}
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(price)
},
},
}
</script>

Expand Down
Loading