Product
Resources
Pierre Burgy
This tutorial is part of the Β« Cooking a Deliveroo clone with Nuxt (Vue.js), GraphQL, Strapi and Stripe Β»:
Note: the source code is available on GitHub: https://github.com/strapi/strapi-tutorials/tree/master/tutorials/deliveroo-clone-nuxt-strapi-tutorial.
All of these dishes look so tasty! What if you could add some of them in a shopping cart?
cart.js
and copy/paste the following code:./frontend/store/cart.js
import Cookies from 'js-cookie'
export const state = () => ({
items: []
})
export const mutations = {
setItems(state, items) {
state.items = items
},
add(state, item) {
const record = state.items.find(i => i.id === item.id)
if (!record) {
state.items.push({
quantity: 1,
...item
})
} else {
record.quantity++
}
Cookies.set('cart', state.items)
},
remove(state, item) {
const record = state.items.find(i => i.id === item.id)
if (record.quantity > 1) {
record.quantity--
} else {
const index = state.items.findIndex(i => i.id === item.id)
state.items.splice(index, 1)
}
Cookies.set('cart', state.items)
},
emptyList(state) {
state.items = []
Cookies.set('cart', state.items)
}
}
export const getters = {
items: state => {
return state.items
},
price: state => {
return state.items.reduce(
(accumulator, item) => accumulator + item.price * item.quantity,
0
)
},
numberOfItems: state => {
return state.items.reduce(
(accumulator, item) => accumulator + item.quantity,
0
)
}
}
To make sure the items stay in the cart even after page reload, you will are also use cookies. So you need to update the nuxtInitServer
function:
index.js
store to get the following:/frontend/store/index.js
import cookieparser from 'cookieparser'
export const actions = {
nuxtServerInit({ commit }, { req }) {
let user = null
let cart = []
if (req && req.headers && req.headers.cookie) {
const parsed = cookieparser.parse(req.headers.cookie)
user = (parsed.user && JSON.parse(parsed.user)) || null
cart = (parsed.cart && JSON.parse(parsed.cart)) || []
}
commit('auth/setUser', user)
commit('cart/setItems', cart)
}
}
Now you want to add the cart to your pages. To do so you are going to create a Cart
component that will be used in your restaurants/_id.vue
and your future orders/checkout.vue
_id.vue
file to get the following code:/frontend/pages/restaurants/_id.vue
<template>
<div>
<a class="uk-button uk-button-primary uk-margin" @click="$router.go(-1)"><span uk-icon="arrow-left"></span> go back</a>
<client-only>
<div uk-grid>
<div class="uk-width-1-3@m">
// Left card displaying dishes
<div v-for="dish in restaurant.dishes" class="uk-margin">
<div class="uk-card uk-card-default">
<div class="uk-card-media-top">
<img :src="'http://localhost:1337/' + dish.image.url" alt="" />
</div>
<div class="uk-card-body">
<h3 class="uk-card-title">{{ dish.name }} <span class="uk-badge">{{ dish.price }}β¬</span></h3>
<p>{{ dish.description }}</p>
</div>
<div class="uk-card-footer">
<button class="uk-button uk-button-primary" @click="addToCart(dish)">Add to cart</button>
</div>
</div>
</div>
</div>
// Right card displaying you cart
<div class="uk-width-expand@m">
// Call a Cart component
<Cart />
</div>
</div>
</client-only>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import Cart from '~/components/Cart.vue'
import restaurantQuery from '~/apollo/queries/restaurant/restaurant'
export default {
data() {
return {
restaurant: Object
}
},
apollo: {
restaurant: {
prefetch: true,
query: restaurantQuery,
variables () {
return { id: this.$route.params.id }
}
}
},
components: {
Cart
},
methods:{
...mapMutations({
addToCart: 'cart/add',
removeFromCart: 'cart/remove'
}),
}
}
</script>
As you may see, you imported a Cart
component. In fact you want to reuse this component in two pages: restaurants/index.vue
and orders/checkout.vue
that you'll create soon
/frontend/components/Cart.vue
and copy/paste the following code:<template>
<div class="uk-card uk-card-default uk-card-body uk-margin" uk-sticky="offset: 20; bottom: true">
<img src="https://assets-ouch.icons8.com/preview/125/6414b067-ba59-46ef-8693-4e190aa466c7.png" class="uk-align-center" height="250" width="250" alt="" />
<div v-if="price > 0">
<table class="uk-table uk-table-striped uk-table-small uk-table-responsive">
<thead>
<tr>
<th>Name</th>
<th>Price (unit)</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr v-for="dish in selectedDishes">
<td class="uk-width-1-2">{{ dish.name }}</td>
<td class="uk-table-shrink">{{ dish.price }}β¬</td>
<td class="uk-table-shrink">{{ dish.quantity }}</td>
<td>
<a class="uk-margin-left"><span class="uk-badge" @click="addToCart(dish)">+</span></a>
<a><span class="uk-badge" style="background: #f0506e;" @click="removeFromCart(dish)">-</span></a>
</td>
</tr>
</tbody>
</table>
<button class="uk-button uk-button-primary" name="button">Process to checkout ({{ price }}β¬)</button>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
methods:{
...mapMutations({
addToCart: 'cart/add',
removeFromCart: 'cart/remove'
})
},
computed: {
id() {
return this.$route.params.id
},
selectedDishes() {
return this.$store.getters['cart/items']
},
price() {
return this.$store.getters['cart/price']
},
numberOfItems() {
return this.$store.getters['cart/numberOfItems']
}
}
}
</script>
Good job! You can now add dishes to your cart, check it out!
π΅ In the next section, you will learn how to setup Stripe for checkout and create orders: https://blog.strapi.io/cooking-a-deliveroo-clone-with-nuxt-vue-js-graphql-strapi-and-stripe-order-and-checkout-part-6-7.