How to create a Function

Functions follow the same pattern as Components. Your function will have a:

  • index.tsx Typescript file
  • package.json
  • and the node_modules folder

Functions don't require a css file

Considering our Medical Prescription example, after the user enters the link to the Prescription and the component submits it using the Ollie Shop Extensions API, our next step is to setup the Function that will listen to the Extension event, and forward this information to the ecommerce provider.

Let's assume that in this example, we will store the prescription data in VTEX Custom Data. On your VTEX Store you'll create a Custom Data app and name it "medical-prescription".

Note to self: The sender is always optimistic.

The second Function required for our customization to work is the one responsible for always enriching the Ollie Shop Cart if the ecommerce provider already has a medical-prescription data.

There are two actions here, one to retrieve information if a Prescription link has already been added, but also to understand if a prescription link is required in the first place. Meaning, if I have a product in my cart, how to identify that this cart requires a prescription?

import type { CustomFunction } from "@ollieshop/sdk";

export const handler: CustomFunction<"cart", "after"> = async (event) => {
  const customData = event.extensions?.platform?.customData as {
    customApps: {
      fields: Record<string, string>
      id: string
    }[]
  };

  const medicalPrescriptionData = customData?.customApps?.find((app) => app.id === "medical-prescription");

  const response = await fetch(
    "https://olliepartnerus.myvtex.com/api/catalog_system/pub/products/search/?fq=productClusterIds:138"
  );

  if (!response.ok) {
    throw new Error("Network response was not ok");
  }

  const data = await response.json();

  let itemsArr: any[] = [];

  let searchItems = data.map((element:any) => element.items).flat();

  const prescriptionUrls = medicalPrescriptionData?.fields.urls.split(',');
  const prescriptionIds = medicalPrescriptionData?.fields.items.split(',');
  event.items.forEach(product => {
    const item = searchItems.find((item: any) => item.itemId === product.id);
    const index = prescriptionIds?.indexOf(product.id);
    let url = prescriptionUrls && prescriptionUrls?.length > 0 && index !== -1 ? prescriptionUrls?.[index!] : ''
    
    item ?
      itemsArr.push({
        ...product,
        requiresPrescription: item ? true : null,
        prescriptionUrl: url,
      }) : itemsArr.push(product);
  });

  (event.items as any) = itemsArr;
  
  return event;
};

Lastly we must go back to our Component and add the finishing touch which is the validation that can now be done due to the function we wrote above that runs when the Cart is loaded. So the Cart is already enriched with the property requiresPrescription.

  return (
    <>
    {cart?.items.map((product: any) => (
      <div className="ml-4 flex flex-1 flex-col justify-between sm:ml-6">
        {product?.requiresPrescription === true && product?.prescriptionUrl === '' && (
            <>
              <p className="mt-1 text-sm font-medium text-gray-900">
                Please provide the URL for your prescription.
              </p>
              <span className="mt-4 flex space-x-2 text-sm text-gray-700" > 
                <input ref={ref} type="text" className="input-class-prescription" required style={{ padding: '10px 20px', border: '1px solid #007BFF', borderRadius: '5px' }} />
                <button onClick={(event: React.FormEvent) => handleSubmit(product)} style={{ backgroundColor: '#007BFF', color: '#fff', padding: '10px 20px', border: 'none', borderRadius: '5px', cursor: 'pointer' }}>Send</button>
                </span>    
            </>
          )} {product?.requiresPrescription === true && product?.prescriptionUrl !== '' && (
            <span className="mt-4 flex space-x-2 text-sm text-gray-700" > 
              Prescription Added - ({product?.prescriptionUrl})
              <button onClick={(event: React.FormEvent) => {
                event.preventDefault();
                updateExtensionAction(cart.id, {id:"update-prescription", scope:"custom", value: { items: ',', urls: ',' } })
              }} style={{ marginLeft: '10px', cursor: 'pointer', color: 'whitrede' }}>
                <XCircleIcon
                  aria-hidden="true"
                  className="flex-shrink-0 text-red-500"
                  height={20}
                  width={20}
                />
              </button>
            </span>
          )} 
        </div>
    ))}
    </>
  );
};