statamic-rad-pack / shopify

Integrate your Shopify products into Statamic using the Admin API.
https://statamic.com/addons/rad-pack/shopify
Other
7 stars 3 forks source link

Cart and add to cart broken from javascript errors #110

Closed austriker27 closed 2 years ago

austriker27 commented 2 years ago

Hey Jack!

I've got a tricky bug I've been trying to solve and is pretty tricky. And its pretty crucial because its stopping orders from customers.

My cart and add to cart functionality is broken and I think I've narrowed it down to the JS from the addon.

What happens

Error from the cart page:

TypeError: can't access property "lineItems", _ref is null
    setCartCount cart.js:27
    promise callback*setCartCount cart.js:27
    <anonymous> site.js:14
    js site.js:81
    __webpack_require__ site.js:253
    <anonymous> site.js:392
    O site.js:287
    <anonymous> site.js:394
    <anonymous> site.js:396

Error from the product page:

Error: [{"field":["checkoutId"],"message":"Checkout does not exist","code":"INVALID"}]
    handleCheckoutMutation webpack-internal:///./node_modules/shopify-buy/index.js:3976
    promise callback*addLineItems webpack-internal:///./node_modules/shopify-buy/index.js:8496
    handleProductFormSubmit webpack-internal:///./resources/js/vendor/shopify/products.js:53
    productForm webpack-internal:///./resources/js/vendor/shopify/products.js:28
    productForm webpack-internal:///./resources/js/vendor/shopify/products.js:26
    <anonymous> webpack-internal:///./resources/js/site.js:40
    js http://rw.test/js/site.js?id=427aa391055cc9dfedd8:81
    __webpack_require__ http://rw.test/js/site.js?id=427aa391055cc9dfedd8:253
    <anonymous> http://rw.test/js/site.js?id=427aa391055cc9dfedd8:392
    O http://rw.test/js/site.js?id=427aa391055cc9dfedd8:287
    <anonymous> http://rw.test/js/site.js?id=427aa391055cc9dfedd8:394
    <anonymous> http://rw.test/js/site.js?id=427aa391055cc9dfedd8:396
products.js:58:13

Note there is a cart id in localstorage if I do this in the console: localStorage.getItem('statamic.shopify.cart.id')

Live URLs where this bug shows up:

Addon version

vannut commented 2 years ago

I dug a little bit into this and found out there seems to be some kind of race-condition when setting/getting/refreshing the chekoutId.

If you visit the cart page in incognito-mode for the first time, the loading process stops midway (actually 75%, but thats just a static image). If you look at the network request being made, we see three requests to the graphql endpoint. The first returns the following 1)

{
  "data": {
    "checkoutCreate": {
      "userErrors": [],
      "checkoutUserErrors": [],
      "checkout": {
        "id": "Z2lkOi8vc2hvcGlmeS9DaGV",
         ...
      }
   }
  }
}

So it creates a checkout id. Which is the same as is stored at the finish of the load of tha page in localstorage.

2nd & 3rd request: respond with an error: message: Variable $id of type ID! was provided invalid value The request header has a big wuery, and the following variables:

{
  "variables": {
      "id": null
  }
}

My guess would be that null should actualy be the checkout id. Refreshing the cart page, in the same window makes the cart show up like it should do.

My feeling says something goes wrong with the sequence in which a non existing checkoutid is processed. If I look at checkout.js I see that on line 15-19 a new checkoutid asynchronously is being requested from the shopify-client. Which afterwards gets stored to the localstorage.

Could it be that the cart is triggering the async checkout from L10; while it does not wait for the checkoutId to return to the javascript? Eg it just exports te checkoutId from L24 which is not yet set by the function?

This would also explain the reference to lineItems which is null; as the cart is not loaded/null.

jackabox commented 2 years ago

Can you try the updates in 1.7.0 (https://github.com/jackabox/statamic-shopify/releases/tag/1.7.0). Specifically #103. Let me know if that helps fix out your issues.

austriker27 commented 2 years ago

Can you try the updates in 1.7.0 (https://github.com/jackabox/statamic-shopify/releases/tag/1.7.0). Specifically #103. Let me know if that helps fix out your issues.

Yup, in debugging @vannut and I saw that PR... I did update the addon (to 1.7.3) and republished the JS files (via php artisan vendor:publish --tag="shopify-scripts").

My composer.json:

"jackabox/statamic-shopify": "^1.7",

To confirm, my checkout.js file looks like the following which matches the updates in #103 as far as I can see.

import client from './client.js'

/**
 * Create the instance of the checkout for the user.
 * Let's first check if this exists or not in the storage.
 * Returns the ID for use elsewhere.
 *
 * @returns {object}
 */
const checkout = async () => {
  // Check if we have found anything in local storage.
  let shopifyCheckout = localStorage.getItem('statamic.shopify.cart.id')

  // If not, let's create a new checkout for the user and set it as the ID.
  if (!shopifyCheckout || shopifyCheckout === 'undefined') {
    const { id } = await client.checkout.create()
    localStorage.setItem('statamic.shopify.cart.id', id)
    shopifyCheckout = id
  }

  return shopifyCheckout
}

const checkoutId = localStorage.getItem('statamic.shopify.cart.id')

export { checkoutId }

export default checkout
jackabox commented 2 years ago

Can you try updating L24 by replacing

const checkoutId = localStorage.getItem('statamic.shopify.cart.id')

with

const checkoutId = checkout()

and see if that fixes up the issues with the checkoutId potentially not being loaded?

austriker27 commented 2 years ago

const checkoutId = checkout()

Unfortunately that's not working locally. I can push up to prod if you think that would be helpful.

Here's a console error I'm getting from the catch inside products.js. (I added a console log here inside products.js)

Error: [{"message":"Variable $checkoutId of type ID! was provided invalid value","locations":[{"line":1,"column":3229}],"extensions":{"value":{},"problems":[{"path":[],"explanation":"Invalid global id{}","message":"Invalid global id{}"}]}}]

In case this helps, I also added a console log inside this catch on cartjs and it shows: [ { "message": "Variable $id of type ID! was provided invalid value", "locations": [ { "line": 1, "column": 3087 } ], "extensions": { "value": {}, "problems": [ { "path": [], "explanation": "Invalid global id{}", "message": "Invalid global id{}" } ] } } ]

austriker27 commented 2 years ago

Let me know how I can help further because this bug is blocking orders for at least a week or so now and Im desperate to fix it!

jackabox commented 2 years ago

@austriker27

A few things to try/rule out:

Firstly, If you open an incognito window, when you initially load the site. Can you check the network tab, you're looking for one of the first loads to the GraphQL endpoint.

Does this either:

  1. Not load at all.
  2. Give you an error of some kind? If so, can you provide me with the network output.

You should get the following if successful

Screenshot 2022-02-03 at 02 21 52

Secondly, how're you loading the scripts in? Are you using the helper or manually loading them?

austriker27 commented 2 years ago

@jackabox

Thanks!

GraphQL:

On a product page (here's a link to this live on the prod), the first graphQL load has an error:

{
    "errors": [
        {
            "message": "Variable $id of type ID! was provided invalid value",
            "locations": [
                {
                    "line": 1,
                    "column": 3087
                }
            ],
            "extensions": {
                "value": {},
                "problems": [
                    {
                        "path": [],
                        "explanation": "Invalid global id `{}`",
                        "message": "Invalid global id `{}`"
                    }
                ]
            }
        }
    ]
}

Loading scripts:

What is the helper? I basically am just importing them in my site.js.

Here's a link to the file in my repo, you have access to my repo so it should work.

import checkout from './vendor/shopify/checkout'
import cart, { setCartCount } from './vendor/shopify/cart'
import productForm from './vendor/shopify/products'
////////////////
// SHOPIFY ////
///////////////
new checkout()
new setCartCount()
new productForm()
new cart()
jackabox commented 2 years ago

@austriker27

~Can you show me your products.js, specifically the imports at the top and the const handleProductFormSubmit function.~

Never mind, I had access, please see the below reply.

jackabox commented 2 years ago

@austriker27 Can you change the L46 on products.js and replace checkoutId with localStorage.getItem('statamic.shopify.cart.id'). Let me know if that works.

jackabox commented 2 years ago

@austriker27 After debugging further this happens in 2 more places, please review #111 for the changes made, specifically checkoutId being changed to localStorage.getItem('statamic.shopify.cart.id'). Feel free to reopen if that's not working.

austriker27 commented 2 years ago

@jackabox Sweet, thank you! I added those changes from PR #111 and can confirm it fixed the "add to cart" bug (Live product page where its fixed)! Thank you!!

That being said, now the cart view doesn't show products that have been added to it. I think there's something wrong with when the table is being appended into the HTML.

Here's the console error I get:

Uncaught (in promise) TypeError: can't access property "appendChild", tableBody is null
    _callee$/</< cart.js:102

EDIT: This line is the problem: const tableBody = document.querySelector('#ss-cart-view table tbody')

And it looks like I don't have that element in my cart template file - which is odd because I haven't changed it recently.

EDIT: Ignore the above - looks like I changed my cart html a long time ago which removed the table element from the html. Oops, I'll fix it!

jackabox commented 2 years ago

@austriker27 Glad to hear you've found out what's the issue. :)

jensolafkoch commented 1 year ago

I had a similar problem, where I always got the "invalid global id" error message from Shopify when trying to delete an item from the cart or update the quantity,

My solution was to change the first parameter of .removeLineItems from checkoutId (which holds the whole Promise when I output it to the console) to the value from localeStorage. Otherwise the id in the request is always empty.

const deleteRowFromStorefront = (row) => {

  //cartDebug ? console.log('in deleteRowFromStorefront:', row) : null
  //cartDebug ? console.log('checkoutId:', checkoutId) : null

  const id = row.getAttribute('data-ss-variant-id')
  const items = []
  items.push(id)

  client.checkout
    //.removeLineItems(checkoutId, items)
    .removeLineItems(localStorage.getItem('statamic.shopify.cart.id'), items)
    .then(({ lineItems, subtotalPriceV2 }) => {
      setCartCount(lineItems)
      setCartSubtotal(subtotalPriceV2.amount)
      bannerMessage(htmlToElements('<p>Item removed successfully</p>'))

      if (lineItems.length === 0) {
        noItemsMessage.classList.remove('hidden')
        cartView.classList.add('hidden')
      }

      row.remove()
    })
}

(Analogous change in updateQtyInStorefront().)

I'm no JS expert but could it be that the change in #111 in cart.js line 26 should have been applied in deleteRowFromStorefront() and updateQtyInStorefront() as well?

Or is maybe something else causing the problem I encountered?

I'm worried that my hack might break something else ... ?

Thanks for any insights!