# Create a Custom Recommendations Component for B2B Stores Using Commerce Einstein APIs

Creating a custom B2B Commerce Einstein recommendations component includes implementing the Activity Tracking API and connecting the component to the Commerce Einstein Webstore Recommendations Connect API resource. This process is for B2B stores on the Aura platform.

Using Connect APIs ensures appropriate product entitlement filtering before recommendations are delivered.

Make sure to reindex after you change associations between buyer groups and products, such as when changing entitlement policies or product-to-buyer group assignments.

Your component must incorporate calls to the Activity Tracking API. When configuring the API:

- Implement both the viewReco and clickReco activities.
- If you replace the Product Detail Purchase Options component with a custom component, implement the viewProduct activity.
- If you add custom add-to-cart functionality, implement the addToCart activity.

You can use custom JavaScript, HTML, and CSS to make front-end modifications. For more information, see the [Lightning Aura Components Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/intro_framework.htm).

1. Create a custom Apex controller.

   1. Go to **Setup** | **Developer Console**.

   2. Select **File** | **New** | **Apex Class**.

   3. Enter a name for the Apex class.

      Make sure to match the component class name for easier identification. For example, `recsController`.

      The controller file (in this case, `recsController.apxc`) opens to show an empty class.

2. Update the controller file, making sure to modify the *orgDomain* and *webstoreId* values to match your storefront domain and ID.

   ```apex
   public with sharing class recsController {
       @AuraEnabled
       public static String getRecs(String recommender, String anchorValues, String cookie) {
           String orgDomain = 'alpinecommerce236.my.salesforce.com';
           String webstoreId = '0ZEB0000000HMNGOA4';
           String endpoint = 'https://'+orgDomain+'/services/data/v55.0/commerce/webstores/
           '+webstoreId+'/ai/recommendations?language=en-US&asGuest=true&recommender='+recommender;
           if (anchorValues.length() > 0) {
               endpoint += '&anchorValues='+anchorValues;
           }

           HttpRequest req = new HttpRequest();
           req.setEndpoint(endpoint);
           req.setHeader('Cookie', cookie);
           req.setMethod('GET');
           req.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());

           Http http = new Http();
           HTTPResponse res = http.send(req);
           return res.getBody();
       }
   }
   ```

3. Update the controller file with the appropriate profile for buyers.

   1. Go to **Setup** | **Custom Code** | **Apex Classes**.

   2. Next to the controller file that you created, click **Security**.

   3. Add the Shopper profile to the enabled profiles column.

   4. Click **Save**.

4. Create an SFDX project in Visual Studio Code and run the `SFDX: Create Aura Component` command.

5. Specify a name for the component.

6. Update the component file with the attribute configuration (for example, `force-app/main/default/aura/<project_name>/<component_name>.cmp`).

   Make sure to use the controller filename that you used when creating the Apex controller, and provide the tag needed for importing activity tracking.

   ```nolang
   <aura:component implements="forceCommunity:availableForAllPageTypes" controller="RecsController" access="global">

       <aura:attribute name="title" type="String" default="" required="true" />
       <aura:attribute name="recommender" type="String" default="RecentlyViewed" required="true" />
       <aura:attribute name="anchorValues" type="String" default="" required="false" />
       <aura:attribute name="uuid" type="String" default=""/>
       <aura:attribute name="loading" type="boolean" default="false"/>
       <aura:attribute name="showProducts" type="boolean" default="false"/>
       <aura:attribute name="products" type="List" default="[]" />

       <!-- Tag needed to import Commerce Activity Tracking-->
       <commerce:activitiesApi aura:id="activitiesApi" />

       <aura:handler name="init" value="{!this}" action="{!c.onLoadComponent}"/>

       <div>
           <aura:if isTrue="{!v.loading}">LOADING...</aura:if>

           <aura:if isTrue="{!v.showProducts}">
               <div class="title">{!v.title}</div>
               <div class="products">
                   <aura:iteration items="{!v.products}" var="product">
                       <div class="product">
                           <img data-pid="{!product.id}" onclick="{!c.handleClickProduct}" src="{!product.defaultImage.url}"/>
                           <a class="name" data-pid="{!product.id}" onclick="{!c.handleClickProduct}">{!product.name}</a>
                       </div>
                   </aura:iteration>
               </div>
           </aura:if>
       </div>

   </aura:component>
   ```

7. Update the metadata in the component’s XML file (for example, `force-app/main/default/aura/<project_name>/<component_name>.cmp-meta.xml`).

   Make sure that the *\<apiVersion>* tag references the latest API version.

   ```xml
   <?xml version="1.0" encoding="UTF-8" ?>
   <AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata">
       <apiVersion>55.0</apiVersion>
       <description>Aura Recommendations Component</description>
       <isExposed>true</isExposed>
       <targets>
           <target>lightningCommunity__Page</target>
           <target>lightningCommunity__Default</target>
       </targets>
   </AuraDefinitionBundle>
   ```

8. Edit your component’s CSS file to add custom styling (for example, `force-app/main/default/aura/<project_name>/<component_name>.css`).

   This sample code includes modifications to the component’s image size and headings.

   ```css
   .THIS div.title {
     text-align: center;
     font-weight: 800;
     font-size: 2em;
   }

   .THIS .products {
     height: 400px;
     text-align: center;
   }
   .THIS .products .product {
     width: 25%;
     height: 450px;
     padding: 10px;
     display: inline-block;
   }
   .THIS .products .product img {
     cursor: pointer;
     max-height: 300px;
     width: auto;
   }
   .THIS .products .product .name {
     display: block;
   }
   ```

9. Edit your component’s `Controller.js` file, making sure to modify the *storeName* value for the current store (for example, `force-app/main/default/aura/<project_name>/<component_name>Controller.js`).

   ```javascript
   ({
     onLoadComponent: function (cmp, event, helper) {
       let pageProductId = helper.getProductDetailProductId();
       if (pageProductId) {
         // Component is on a product detail page; show Similar Products recommender
         cmp.set("v.title", "Similar Products");
         cmp.set("v.recommender", "SimilarProducts");
         cmp.set("v.anchorValues", pageProductId);
       } else {
         // Show Recently Viewed recommender
         cmp.set("v.title", "Recently Viewed");
         cmp.set("v.recommender", "RecentlyViewed");
         cmp.set("v.anchorValues", "");
       }
       helper.loadProductRecommendations(cmp, event, helper);
     },

     // send a clickReco activity and navigate to the product detail page
     handleClickProduct: function (cmp, event, helper) {
       let productId = event.currentTarget.getAttribute("data-pid");
       let trackClickReco = cmp.find("activitiesApi").trackClickReco;
       let recName = helper.recommenderNames[cmp.get("v.recommender")];
       let uuid = cmp.get("v.uuid");
       let products = cmp.get("v.products");
       let product = products.filter((p) => p.id === productId)[0];
       let productToSend = {
         id: product.id,
         price: product.prices ? product.prices.listPrice : undefined,
       };
       trackClickReco(recName, uuid, productToSend);

       // navigate to the product page
       let storeName = "AlpineB2B";
       let productName = product.name || "detail";
       let newHref = `/${storeName}/s/product/${productName}/${productId}`;
       window.location.href = newHref;
     },
   });
   ```

10. Edit your component’s `Helper.js` file, using this example for guidance (for example, `force-app/main/default/aura/<project_name>/<component_name>Helper.js)`.

    ```javascript
    ({
      loadProductRecommendations: function (cmp, event, helper) {
        try {
          cmp.set("v.loading", true);
          var action = cmp.get("c.getRecs");
          action.setParams({
            recommender: cmp.get("v.recommender"),
            anchorValues: cmp.get("v.anchorValues"),
            cookie: document.cookie,
          });
          // Create a callback that is executed after
          // the server-side action returns
          action.setCallback(this, function (response) {
            var state = response.getState();
            if (state === "SUCCESS") {
              try {
                let data = JSON.parse(response.getReturnValue());
                let products = data.productPage.products;
                // Keep it simple, only show 4 products
                cmp.set("v.products", products.slice(0, 4));
                cmp.set("v.uuid", data.uuid);
                cmp.set("v.loading", false);
                let showProducts = products.length > 0;
                cmp.set("v.showProducts", showProducts);
                // Only send the viewReco activity when we display the product
                // recommendations
                if (showProducts) {
                  helper.sendViewRecoActivity(cmp, helper);
                }
              } catch (err) {
                console.error("Error fetching recommendations", err);
                cmp.set("v.loading", false);
              }
            }
          });
          $A.enqueueAction(action);
        } catch (error) {
          console.error("Failed to load recommendations: ", error);
          cmp.set("v.loading", false);
        }
      },
      // The recommender names we pass into the Connect API are in a different format
      // than the recommender names we pass into the activities api.
      recommenderNames: {
        RecentlyViewed: "recently-viewed",
        SimilarProducts: "similar-products",
        MostViewedByCategory: "most-viewed-by-category",
        TopSelling: "top-selling",
        Upsell: "upsell",
      },

      formatPrice: function (price, curr) {
        return new Intl.NumberFormat("en-US", { style: "currency", currency: curr }).format(price);
      },

      sendViewRecoActivity: function (cmp, helper) {
        let trackViewReco = cmp.find("activitiesApi").trackViewReco;
        let recName = helper.recommenderNames[cmp.get("v.recommender")];
        let products = cmp.get("v.products").map((p) => ({ id: p.id }));
        let uuid = cmp.get("v.uuid");
        trackViewReco(recName, uuid, products);
      },

      getProductDetailProductId: function () {
        let pageProductIdMatch = window.location.href.match(new RegExp("01t[a-zA-Z0-9]{15}"));
        let pid = pageProductIdMatch ? pageProductIdMatch[0] : null;
        return pid;
      },
    });
    ```

11. After you create the custom Einstein Recommendations component, deploy it from Visual Studio Code and place it in your store with Experience Builder.

12. Before publishing your site from Experience Builder, click **Preview** to see how the custom component looks in a desktop browser window and on a mobile device.

**See Also**

- [Commerce Einstein APIs](https://developer.salesforce.com/docs/atlas.en-us.b2b_b2c_comm_dev.meta/b2b_b2c_comm_dev/b2b_b2c_comm_einstein_apis.htm)
- [Prepare Commerce Einstein to Use Custom Components](https://developer.salesforce.com/docs/atlas.en-us.b2b_b2c_comm_dev.meta/b2b_b2c_comm_dev/b2b_b2c_comm_prepare_einstein_custom_comps.htm)
- [Commerce Einstein Activity Tracking API](https://developer.salesforce.com/docs/atlas.en-us.b2b_b2c_comm_dev.meta/b2b_b2c_comm_dev/b2b_b2c_comm_activity_tracking_api.htm)
- [B2B Commerce Einstein Product Recommendations API](https://developer.salesforce.com/docs/atlas.en-us.b2b_b2c_comm_dev.meta/b2b_b2c_comm_dev/b2b_b2c_comm_einstein_b2b_reco_api_ref.htm)
- [Tracked Data](https://help.salesforce.com/s/articleView?id=commerce.comm_einstein_data.htm)
- [Commerce Einstein Webstore Recommendations](https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/connect_resources_commerce_webstore_ai_recommendations.htm)
