Skip to content

Commit

Permalink
WIP - GraphQL Cart
Browse files Browse the repository at this point in the history
  • Loading branch information
royduin committed Nov 3, 2023
1 parent 2330199 commit 34c8118
Show file tree
Hide file tree
Showing 27 changed files with 305 additions and 199 deletions.
10 changes: 10 additions & 0 deletions resources/js/callbacks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { useLocalStorage, StorageSerializers } from '@vueuse/core'

Vue.prototype.scrollToElement = (selector) => {
let el = window.document.querySelector(selector)
window.scrollTo({
top: el.offsetTop,
behavior: 'smooth',
})
}

Vue.prototype.refreshCart = async function (data, response) {
useLocalStorage('graphql_cart', null, { serializer: StorageSerializers.object }).value = Object.values(response.data.data)[0]
window.app.$emit('cart-refreshed')
// TODO: This refresh is to late and uses the old data? Should we somehow
// async "wait" for it? Or should we use a reactive global store like
// before with "window.app.cart"?
}
45 changes: 0 additions & 45 deletions resources/js/components/Cart/Cart.vue

This file was deleted.

3 changes: 3 additions & 0 deletions resources/js/components/Cart/mixins/GetCart.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// TODO: It should be possible to remove this whole file?
import { useLocalStorage } from '@vueuse/core'
import { cart, refresh as refreshCart, clear as clearCart } from '../../../stores/useCart'
import { user } from '../../../stores/useUser'
Expand Down Expand Up @@ -29,6 +30,7 @@ export default {
return mask.value
},

// TODO: Test/implement this with GraphQL
async linkUserToCart() {
await magentoUser
.put('guest-carts/' + mask.value, {
Expand All @@ -44,6 +46,7 @@ export default {
},

expiredCartCheck(error) {
// TODO: Test/implement this with GraphQL
if (error.response.data?.parameters?.fieldName == 'quoteId' || error.response.status === 404) {
clearCart()
Notify(window.config.translations.errors.cart_expired, 'error')
Expand Down
51 changes: 0 additions & 51 deletions resources/js/components/Coupon/Coupon.vue

This file was deleted.

22 changes: 16 additions & 6 deletions resources/js/components/Graphql.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export default {
callback: {
type: Function,
},
rerunEvent: {
type: String,
},
},
data: () => ({
Expand All @@ -35,19 +38,26 @@ export default {
}),
render() {
return this.$scopedSlots.default({
data: this.data,
runQuery: this.runQuery,
})
return this.$scopedSlots.default(this)
},
created() {
if (!this.getCache()) {
this.runQuery()
if (this.rerunEvent) {
window.app.$on(this.rerunEvent, () => {
this.run()
})
}
this.run()
},
methods: {
run() {
if (!this.getCache()) {
this.runQuery()
}
},
getCache() {
if (this.cache === undefined) {
return false
Expand Down
1 change: 1 addition & 0 deletions resources/js/components/Product/AddToCart.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script>
// TODO: Should we also migrate this to GraphQL so we can use the cart response directly and do not need another request?
import GetCart from './../Cart/mixins/GetCart'
import InteractWithUser from './../User/mixins/InteractWithUser'
Expand Down
2 changes: 2 additions & 0 deletions resources/js/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import './polyfills'
import { useLocalStorage, StorageSerializers } from '@vueuse/core'
import useCart from './stores/useCart'
import useUser from './stores/useUser'
import useMask from './stores/useMask'
import useSwatches from './stores/useSwatches'
import './vue'
import { computed } from 'vue'
Expand Down Expand Up @@ -61,6 +62,7 @@ function init() {
loadAutocomplete: false,
cart: useCart(),
user: useUser(),
mask: useMask(),
swatches: useSwatches(),
checkout: {
step: 1,
Expand Down
5 changes: 5 additions & 0 deletions resources/js/stores/useCart.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ let hasRefreshed = false
let isRefreshing = false

export const refresh = async function () {
// TODO: We don't need this anymore as we're using the GraphQL responses
// but we still need some logic from here like clearing the addresses
console.log('TODO! /api/cart is removed')
return true

hasRefreshed = true
if (!mask.value && !token.value) {
cartStorage.value = {}
Expand Down
3 changes: 0 additions & 3 deletions resources/js/vue-components.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import toggler from './components/Elements/Toggler.vue'
Vue.component('toggler', toggler)

import cart from './components/Cart/Cart.vue'
Vue.component('cart', cart)
import addToCart from './components/Product/AddToCart.vue'
Vue.component('add-to-cart', addToCart)

Expand All @@ -27,7 +25,6 @@ Vue.component('images', images)
Vue.component('autocomplete', () => import('./components/Search/Autocomplete.vue'))
Vue.component('login', () => import('./components/Checkout/Login.vue'))
Vue.component('listing', () => import('./components/Listing/Listing.vue'))
Vue.component('coupon', () => import('./components/Coupon/Coupon.vue'))
Vue.component('checkout', () => import('./components/Checkout/Checkout.vue'))
Vue.component('checkout-success', () => import('./components/Checkout/CheckoutSuccess.vue'))
Vue.component('popup', () => import('./components/Popup.vue'))
38 changes: 6 additions & 32 deletions resources/views/cart/coupon.blade.php
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
<div class="bg-white rounded-lg border">
<div class="p-3">
<h3 class="text-lg leading-6 font-medium text-gray-900">
@lang('Apply coupon code')
</h3>
<coupon v-slot="{ cart, removeCoupon, couponCode, inputEvents, applyCoupon }">
<div>
<form class="mt-5 flex" @submit.prevent="applyCoupon">
<x-rapidez::input
:label="false"
name="couponCode"
placeholder="Coupon code"
v-on="inputEvents"
v-bind:value="couponCode"
v-bind:disabled="$root.loading"
/>
<div class="bg-white rounded-lg border p-3">
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-5">
@lang('Apply coupon code')
</h3>

<x-rapidez::button type="submit" class="ml-3 sm:text-sm">
@lang('Apply')
</x-rapidez::button>
</form>

<div class="relative rounded-md" v-if="cart.discount_name && cart.discount_amount < 0">
<div class="flex items-center">
<button v-on:click="removeCoupon">
<x-heroicon-s-x-mark class="h-4 w-4 text-black-400"/>
</button>
@lang('Discount'): @{{ cart.discount_name }}
</div>
</div>
</div>
</coupon>
</div>
@include('rapidez::cart.coupon.add')
@include('rapidez::cart.coupon.list')
</div>
23 changes: 23 additions & 0 deletions resources/views/cart/coupon/add.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<graphql-mutation
:query="'mutation ($cart_id: String!, $coupon_code: String!) { applyCouponToCart(input: { cart_id: $cart_id, coupon_code: $coupon_code }) { cart { ' + config.queries.cart + ' } } }'"
:variables="{ cart_id: window.app.mask, coupon_code: '' }"
:notify="{ message: config.translations.cart.coupon.applied }"
:clear="true"
:watch="false"
:callback="refreshCart"
v-slot="{ mutate, variables }"
>
<form v-on:submit.prevent="mutate" class="flex gap-3">
<x-rapidez::input
:label="false"
name="couponCode"
placeholder="Coupon code"
v-model="variables.coupon_code"
v-bind:disabled="$root.loading"
required
/>
<x-rapidez::button type="submit" class="sm:text-sm">
@lang('Apply')
</x-rapidez::button>
</form>
</graphql-mutation>
15 changes: 15 additions & 0 deletions resources/views/cart/coupon/list.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template v-if="data.cart.applied_coupons.length" v-for="coupon in data.cart.applied_coupons">
<graphql-mutation
:query="'mutation ($cart_id: String!) { removeCouponFromCart(input: { cart_id: $cart_id }) { cart { ' + config.queries.cart + ' } } }'"
:variables="{ cart_id: window.app.mask }"
:callback="refreshCart"
v-slot="{ mutate }"
>
<div class="flex">
<button v-on:click="mutate">
<x-heroicon-s-x-mark class="h-4 w-4 text-black-400"/>
</button>
@{{ coupon.code }}
</div>
</graphql-mutation>
</template>
25 changes: 25 additions & 0 deletions resources/views/cart/item.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{-- TODO: Refactor to a table just like the checkout theme as things don't always line up as it should --}}
<div v-for="(item, index) in data.cart.items" class="relative flex gap-5 border-b py-3 max-lg:flex-col lg:items-center">
<div class="flex flex-1 items-center gap-5">
<a class="w-20" :href="item.product.url_key + item.product.url_suffix | url">
<img class="mx-auto" :alt="item.product.name" :src="'/storage/{{ config('rapidez.store') }}/resizes/80x80/magento' + item.product.image.url.replace(config.media_url, '') + '.webp'" height="80" />
</a>
<div class="flex flex-col items-start gap-1">
<a class="font-bold" :href="item.product.url_key + item.product.url_suffix | url" dusk="cart-item-name">
@{{ item.product.name }}
</a>
<div v-for="option in item.configurable_options">
@{{ option.option_label }}: @{{ option.value_label }}
</div>
<div v-for="option in item.customizable_options">
@{{ option.label }}: @{{ option.values[0].label || option.values[0].value }}
</div>
@include('rapidez::cart.item.remove')
</div>
</div>
<div class="flex items-center justify-between gap-5">
@{{ item.prices.price_including_tax.value | price }}
@include('rapidez::cart.item.quantity')
@{{ item.prices.row_total_including_tax.value | price }}
</div>
</div>
25 changes: 25 additions & 0 deletions resources/views/cart/item/quantity.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<graphql-mutation
:query="'mutation ($cart_id: String!, $cart_item_id: Int, $quantity: Float) { updateCartItems(input: { cart_id: $cart_id, cart_items: [{ cart_item_id: $cart_item_id, quantity: $quantity }] }) { cart { ' + config.queries.cart + ' } } }'"
:variables="{ cart_id: window.app.mask, cart_item_id: item.id, quantity: item.quantity }"
:callback="refreshCart"
:watch="false"
v-slot="{ mutate, variables }"
>
<form v-on:submit.prevent="mutate" class="flex items-center gap-1">
<x-rapidez::input
name="qty"
type="number"
:label="false"
v-model="variables.quantity"
v-bind:dusk="'qty-'+index"
{{-- TODO: We don't need these importants with Tailwind merge and center isn't really center with type number --}}
class="!w-14 !px-1 text-center"
{{-- TODO: We can't get these values with GraphQL --}}
{{-- ::min="item.min_sale_qty > item.qty_increments ? item.min_sale_qty : item.qty_increments" --}}
{{-- ::step="item.qty_increments" --}}
/>
<x-rapidez::button type="submit" v-bind:dusk="'item-update-'+index" :title="__('Update')">
<x-heroicon-s-arrow-path class="h-4 w-4" />
</x-rapidez::button>
</form>
</graphql-mutation>
11 changes: 11 additions & 0 deletions resources/views/cart/item/remove.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<graphql-mutation
:query="'mutation ($cart_id: String!, $cart_item_id: Int) { removeItemFromCart(input: { cart_id: $cart_id, cart_item_id: $cart_item_id }) { cart { ' + config.queries.cart + ' } } }'"
:variables="{ cart_id: window.app.mask, cart_item_id: item.id }"
:notify="{ message: item.product.name+' '+config.translations.cart.remove }"
:callback="refreshCart"
v-slot="{ mutate }"
>
<button :disabled="$root.loading" v-on:click="mutate" title="@lang('Remove')" :dusk="'item-delete-'+index" class="hover:underline">
@lang('Remove')
</button>
</graphql-mutation>
Loading

0 comments on commit 34c8118

Please sign in to comment.