vendure-ecommerce / storefront-qwik-starter

An e-commerce storefront starter built with Qwik and Vendure
https://qwik-storefront.vendure.io
227 stars 86 forks source link

Unsure how to add customfields to OrderLine mutation #154

Closed porl closed 6 months ago

porl commented 7 months ago

I've been evaluating this starter template for a custom shop site and have now successfully worked out how to determine if a product or variant has a customfield attached to it.

I am basically following the "configurable products" server side tutorial here https://docs.vendure.io/guides/how-to/configurable-products/ (though with slightly different field names).

How do I add a text field to the product page with the correct code to work with the addItemToOrder mutation? I see the generated schema-shop.graphql file contains the correct field:

type OrderLineCustomFields {
  customText: String
}

input OrderLineCustomFieldsInput {
  customText: String
}

and the graphql-shop.ts file has types associated with it too. Do I need to change relevant functions in order.ts? I assume I change it there somehow and then also change the call from products/index.tsx to match, but I'm not sure what to put in for the parameter type etc.

Sorry for the ignorant question, I'm moving across from an old php-based shop to vendure as a headless system and there are a lot of differences to learn at once!

gioboa commented 7 months ago

Hi @porl If I understand correctly you have a custom field in the order items. So every time you add an item in the order you need to pass this information as well. To achieve this result you need to change the correct gql query and pass this extra information into the right methods. i.e addItemToOrder You can test your gql query in the Vendure playground and then implement the functionality in the frontend.

porl commented 7 months ago

Yes, that's what I'm trying to do. Basically copy the "engravable" product from the vendure docs to this qwik starter template.

What I don't understand is how to do it with all the auto-generating code etc. What parts would I change to make the typescript generate correctly for example?

gioboa commented 7 months ago

You need to change this variable and generate the correct types based on your environment

porl commented 7 months ago

Hi @gioboa

Yes, that's correct. I did as you suggested and tried the query manually in the playground and that works easily enough:

mutation {
  addItemToOrder(productVariantId: 119, quantity: 1, customFields: {customText: "customised text"}) {
    ...on Order {
      id
      code
      totalQuantity
      totalWithTax
      lines {
        productVariant {
          name
        }
        customFields {
          customText
        }
        quantity
        linePriceWithTax
      }
    }
    ...on ErrorResult {
      errorCode
      message
    }
  }
}

Trying to add this extra parameter to src/providers/shop/orders/order.ts is where I'm stuck though. I tried just adjusting the gql mutation string template, but that did not work by itself. Trying to edit export const addItemToOrderMutation = async ... I think is what I need to do, but I don't know what needs to go there. It references generated/graphql.ts AddItemToOrderMutation but that has no reference to the custom field parameter. The fact that this file is in the generated directory makes me unsure if I should touch it or it is generated from something elsewhere I need to change instead.

Sorry if it is unclear - I understand what needs to be sent to the Vendure backend, but the qwik starter template is using a lot of autogenerated code and I don't understand how to add in this custom field correctly.

gioboa commented 7 months ago

Did you regenerate the schemas from your backend environment?

porl commented 7 months ago

Sorry, I'm not sure. I'm using plain Vendure in the backend and the Vendure Qwik Starter template in the frontend.

Looking at generated/schema.graphql I can see ProductCustomFields contains printfulProductId which I think is from the standard test vendure demo site, and OrderLine customFields is just set to JSON (implying it does not have any).

How do I regenerate this file? I assume I also need to regenerate the graphql.ts file in the same directory. All other files show modifications in git so I assume they are being generated when the pnpm start command is run, but those two files are unchanged. Is there a pnpm command to rebuild these from the vendure backend?

Update: I found and ran the command pnpm run generate-shop and nothing seemed to change. Ran it again and this time it did update the graphql-shop.ts and schema-shop.graphql files.

Hopefully this will get me in the right direction. I assume I need to update the order.ts mutation gql template string to match? It is currently as below:

gql`
    mutation addItemToOrder($productVariantId: ID!, $quantity: Int!) {
        addItemToOrder(productVariantId: $productVariantId, quantity: $quantity) {
            ...OrderDetail
            ... on ErrorResult {
                errorCode
                message
            }
        }
    }
`;

How do I add an (optional) customfields parameter to this? The graphql.ts file shows the following:

export type AddItemToOrderMutationVariables = Exact<{
    productVariantId: Scalars['ID'];
    quantity: Scalars['Int'];
}>;

But I'm not sure if this should be regenerated somehow or if it needs to be manually edited (or if I'm chasing a red-herring).

Thanks for your patience

michaelbromley commented 6 months ago

Hi @porl

You are on the right track. You need to make sure that when you run pnpm run generate-shop, you make sure it is pointing at the url of the API that contains you custom fields on OrderLine.

If we look at the codegen-shop.ts script, we can see that the URL to be used is determined by environment variables, and based on that, one of these 3 URLs will be used.

So it looks like if you set the IS_LOCAL env var, it should do the codegen from your localhost which is probably what you want.

IS_LOCAL=true pnpm run generate-shop

Let me know if that works

porl commented 6 months ago

Hi @michaelbromley

Very sorry for the slow replies. Currently sick so only jumping on here every so often while I recover.

I didn't think to try setting the environment variable, but earlier on I did set all three URLs to be the same (pointing to localhost).

As I mentioned before, some things changed to match, however I noticed the routes/products/[...slug]/index.tsx file references the following:

import { Order, OrderLine, Product } from '~/generated/graphql';

and

import { addItemToOrderMutation } from '~/providers/shop/orders/order';

which references

import {
    ActiveOrderQuery,
    AddItemToOrderMutation,
    AdjustOrderLineMutation,
    CreateAddressInput,
    CreateCustomerInput,
    Order,
    OrderByCodeQuery,
    RemoveOrderLineMutation,
    SetCustomerForOrderMutation,
    SetOrderShippingAddressMutation,
    SetOrderShippingMethodMutation,
} from '~/generated/graphql';

as well as containing the 'hardcoded' gql template statements I pasted in the last reply.

I assumed this meant that the generated/graphql.ts file was supposed to be regenerated from the server as well, but perhaps this is instead an old version and those files should be updated to point to the `graphql-shop.ts file instead?

For reference, the graphql-shop.ts file now contains the following:

export type OrderLine = Node & {
  __typename?: 'OrderLine';
  createdAt: Scalars['DateTime']['output'];
  customFields?: Maybe<OrderLineCustomFields>;
  /** The price of the line including discounts, excluding tax */
...
}

and

export type OrderLineCustomFields = {
  __typename?: 'OrderLineCustomFields';
  customText?: Maybe<Scalars['String']['output']>;
};

export type OrderLineCustomFieldsInput = {
  customText?: InputMaybe<Scalars['String']['input']>;
};
porl commented 6 months ago

Finally back at work. I changed the providers and routes files to point to generated/graphql-shop.ts rather than generated/graphql.ts and the site appears to still work correctly.

Using the following code on a product page I can get the site to tell me if a product has the customisable field (which would then be used to display the text field):

                {productSignal.value.customFields?.allowsCustomText && (
                  <div>CUSTOMISABLE!</div>
                )}

Unfortunately I am still stuck here - I don't know what to change in the order.ts or similar files to send the correct info back as a mutation (let alone display it in cart, make it editable etc). The Vendure docs show in detail what is needed in the backend for an "engravable" product, however it would be really helpful if one or more of the storefront starters showed how to integrate this.

michaelbromley commented 6 months ago

Hi @porl

So to set the value of the custom field on the OrderLine, you need to find the addItemToOrder mutation and add the custom field value(s) as the "customFields" input argument to the mutation. An example is given here: https://docs.vendure.io/guides/how-to/configurable-products/#setting-the-custom-field-value

That means changing these lines to accept the "customFields" input:

    mutation addItemToOrder($productVariantId: ID!, $quantity: Int!, $customFields: OrderLineCustomFieldsInput) {
        addItemToOrder(productVariantId: $productVariantId, quantity: $quantity, customFields: $customFields) {
            ...OrderDetail
            ... on ErrorResult {
                errorCode
                message
            }
        }
    }

and likewise add the argument to this function

porl commented 6 months ago

Thank you! That has given me enough to go by I believe. I got some test text sent through a custom field. I still need to build the interface but that part should be independent from all this so I appreciate all the help.

porl commented 6 months ago

Sorry to reopen - submitting the custom field to the backend works perfectly (I can use a text field to add custom text to a product and the order shows the custom text in the admin side), however I can't seem to get the field info back for pages such as the cart/checkout etc.

I noticed in the graphql-shop.ts generated file there is the following (chopped for brevity):

export const OrderDetailFragmentDoc = gql`
    fragment OrderDetail on Order {
  __typename
  id
  code
  active
  createdAt
  state
  ...
  lines {
    id
    ...
    productVariant {
      ...
      product {
        id
        slug
        customFields {
          allowsCustomText
        }
      }
    }
    customFields {
      customText
    }
  }
}
    `;

Which has the custom fields as expected. However the providers/shop.orders/order.ts contains (again chopped for brevity):

gql`
    fragment OrderDetail on Order {
        __typename
        id
        code
        active
        createdAt
        state
        currencyCode
        totalQuantity
        subTotal
        subTotalWithTax
        taxSummary {
            description
            taxRate
            taxTotal
        }
        shippingWithTax
        totalWithTax
        customer {
            id
            firstName
            lastName
            emailAddress
        }
        shippingAddress {
            fullName
            streetLine1
            streetLine2
            company
            city
            province
            postalCode
            countryCode
            phoneNumber
        }
        shippingLines {
            shippingMethod {
                id
                name
            }
            priceWithTax
        }
        lines {
            id
            ...
            productVariant {
                ...
                product {
                    id
                    slug
                }
            }
        }
    }
`;

Which lacks the custom fields. Trying to add them in here to match however, gives me the warning

Warning: fragment with name OrderDetail already exists.
graphql-tag enforces all fragment names across your application to be unique

and there is no sign of the custom field values (or keys) in the order object on the front end. For a quick test, I am just using JSON.stringify(line) on the order line object in the CartContents.tsx component but only can get the "default" values - e.g. for the product I only have keys for id and slug.

Is there a reason that order.ts doubles up this gql code? I assume it should be overriding the default in order to only get info we want, but then how can I avoid the warning? And why is the warning not there when the content is exactly as it was without the custom fields? That is still not the same as the graphql-shop one. Am I in the wrong spot trying to add those fields to it? As I mentioned previously, I updated the imports at the top to point to graphql-shop instead of just graphql.

porl commented 6 months ago

Update - still getting that warning, but after stopping and starting the server a couple of times it at least has the fields in the line object.

I would like to clean things up so I don't get the warning, but at least I can keep working.

michaelbromley commented 6 months ago

Hi, I'm not sure why there are 2 almost identical fragments. The query & fragment in admin/order.ts does not even appear to be used currently.

To be honest I think the graphql setup in this project is way over-complicated and in need of a re-think. It is too difficult to figure out what to change. I recently discovered https://gql-tada.0no.co and started using it on another Vendure project and it is much, much better and simpler to use. Maybe you can check that out if you like.

porl commented 6 months ago

Thanks for the recommendation. At least I know I'm not the only one that is finding the graphql generators etc complicated!

It would be great if there were a "quick starter template" that was a little more directly made for the basic Vendure install. I'm not generally a fan of hard-coding things, but maybe for a quick template it would be better to just manually create (or generate externally) the gql statements? Having auto-generated code is great for flexibility, but usually if I'm looking for a starter template I am looking to see how things work and learn a platform before I extend things for myself.

Having something more "direct" and then maybe some examples on how to add fields (taking the Vendure docs examples of custom fields and configurable products I think would be more than enough to get someone like me going. I think even the generation of the typescript functions could be pulled out to be external and just show similar info to the Vendure docs for manually adding more.

Obviously I'm not trying to imply any of this as demands or "you should do this!". I totally understand the amount of work that goes into these things. I just thought it was a good opportunity to throw in my ideal setup thoughts while the possibility of rethinking things is there.

Thanks again for your help! I'll mark this as closed (again) as the warning message, while unfortunate, is not stopping me from achieving what the thread is about.