This guide covers building a wishlist page on headless storefronts.

Step 1: Add wrapper functions that call Swym APIs to support the Wishlist Page

📘

Code Overview:

callDeleteProductFromWishlist(): This function calls the update-ctx API and uses the 'd' parameter (for delete) to delete a product from a list.

callFetchPublicListAPI(): This function calls the markPublic API to mark a list 'public' in order to allow sharing it.

callShareEmailAPI(): This function calls the emailList API to share a publicly marked list to a user email from Swym.

getSwymLocalStorage: These component is found in our utilities file shared in the first page of this guide. If you haven't already, click here to view our guide.

callGenrateRegidAPI: This component is found in the store-api.js file shared in the second page of this guide. If you haven't already, click here to view our guide.

SWYM_CONFIG: This file needs to be setup to store API keys, endpoints, etc. If you haven't set it up already, click here.

callFetchWishlistedProductAPI(): This function calls the fetch-list-with-contents API to fetch all lists of a user with their respective products.

import {getSwymLocalStorage} from '../Utils/Utils';
import {callGenrateRegidAPI} from './store-apis';
import {v4 as uuidv4} from 'uuid';
import SWYM_CONFIG from '../swym.config';

const SWYM_PID = SWYM_CONFIG.PID;
/*
  @notice: get wishlisted product by regid
  @dev:    for single list use this to check if product is wishlisted or not.
  @author: swym
  @return: array of wishlisted products 
*/
export const callFetchWishlistedProductAPI = async () => {
  var myHeaders = new Headers();
  myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');

  const swymConfig = getSwymLocalStorage();
  if (!swymConfig || !swymConfig.regid) {
    await callGenrateRegidAPI({
      username: null,
      uuid: uuidv4(),
      cb: () => callFetchWishlistedProductAPI(),
    });
  } else {
    var urlencoded = new URLSearchParams();
    urlencoded.append('regid', swymConfig.regid);
    urlencoded.append('sessionid', swymConfig.sessionid);

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: urlencoded,
      redirect: 'follow',
    };
    return fetch(
      `${
        SWYM_CONFIG.ENDPOINT
      }api/v3/lists/fetch-list-with-contents?pid=${encodeURIComponent(SWYM_PID)}`,
      requestOptions,
    )
      .then((response) => response.json())
      .then((result) => {
        return result;
      })
      .catch((error) => {
        console.log('error', error);

        return error;
      });
  }
};

/*
  @notice: to remove product from wishlisted items
  @dev:    delete product from swym wishlisted products
  @author: swym
*/
export const callDeleteProductFromWishlist = async (productData) => {
  var myHeaders = new Headers();
  myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');

  const swymConfig = getSwymLocalStorage();

  var urlencoded = new URLSearchParams();
  urlencoded.append('regid', swymConfig.regid);
  urlencoded.append('sessionid', swymConfig.sessionid);
  urlencoded.append('lid', productData.lid);
  urlencoded.append(
    'd',
    `[{ "epi":${productData.epi}, "empi": ${productData.empi}, "du":"${productData.du}"}]`,
  );

  var requestOptions = {
    method: 'POST',
    headers: myHeaders,
    body: urlencoded,
    redirect: 'follow',
  };
  return fetch(
    `${SWYM_CONFIG.ENDPOINT}api/v3/lists/update-ctx?pid=${encodeURIComponent(
      SWYM_PID,
    )}`,
    requestOptions,
  )
    .then((response) => response.json())
    .then((result) => {
      return result;
    })
    .catch((error) => {
      console.log('error', error);

      return error;
    });
};

/*
  @notice: -
  @dev:    to mark the list id as public for list sharing
  @author: swym
  @params: lid - list id  of the particular wishlist
*/

export const callFetchPublicListAPI = async (lid) => {
  var myHeaders = new Headers();
  myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');

  const swymConfig = getSwymLocalStorage();
  if (!swymConfig || !swymConfig.regid) {
    await callGenrateRegidAPI({
      username: null,
      uuid: uuidv4(),
      cb: () => callFetchPublicListAPI(lid),
    });
  } else {
    // console.log("here in wishlist page")
    var urlencoded = new URLSearchParams();
    urlencoded.append('regid', swymConfig.regid);
    urlencoded.append('sessionid', swymConfig.sessionid);
    urlencoded.append('lid', lid);

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: urlencoded,
      redirect: 'follow',
    };
    return fetch(
      `${SWYM_CONFIG.ENDPOINT}api/v3/lists/markPublic?pid=${encodeURIComponent(
        SWYM_PID,
      )}`,
      requestOptions,
    )
      .then((response) => response.json())
      .then((result) => {
        return result;
      })
      .catch((error) => {
        console.log('error', error);

        return error;
      });
  }
};

/*
  @notice: an email is sent to the given email with the selected list products
  @dev:    share wishlist via email
  @author: swym
  @params: publicLid - the list id which has been marked public previously
  @params: senderName - the name of the sender/username of the person who has logged in  
  @params: emailValue - recipent's email address  
*/

export const callShareEmailAPI = async (publicLid, senderName, emailValue) => {
  var myHeaders = new Headers();
  myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');

  const swymConfig = getSwymLocalStorage();
  if (!swymConfig || !swymConfig.regid) {
    await callGenrateRegidAPI({
      username: null,
      uuid: uuidv4(),
      cb: () => callShareEmailAPI(publicLid, senderName, emailValue),
    });
  } else {
    var urlencoded = new URLSearchParams();
    urlencoded.append('regid', swymConfig.regid);
    urlencoded.append('sessionid', swymConfig.sessionid);
    urlencoded.append('lid', publicLid);
    urlencoded.append('fromname', senderName);
    urlencoded.append('toemail', emailValue);

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: urlencoded,
      redirect: 'follow',
    };
    return fetch(
      `${SWYM_CONFIG.ENDPOINT}api/v3/lists/emailList?pid=${encodeURIComponent(
        SWYM_PID,
      )}`,
      requestOptions,
    )
      .then((response) => response.json())
      .then((result) => {
        return result;
      })
      .catch((error) => {
        console.log('error', error);

        return error;
      });
  }
};

/*
  @notice: a link is copied to clipboard which can be used to share the wishlist
  @dev:    share wishlist via copy link
  @author: swym
  @params:   publicLid - the list id which has been marked public previously
  @params:   the medium through which we are sharing the list(copyLink, email etc)
  @params:   sharedurl - 
  @params:   shareListSenderName - the name of the sender/username of the person who has logged in
*/

export const callCopyLinkAPI = async (
  publicLid,
  medium,
  sharedurl,
  shareListSenderName,
) => {
  var myHeaders = new Headers();
  myHeaders.append('Content-Type', 'application/x-www-form-urlencoded');

  const swymConfig = getSwymLocalStorage();
  if (!swymConfig || !swymConfig.regid) {
    await callGenrateRegidAPI({
      username: null,
      uuid: uuidv4(),
      cb: () =>
        callCopyLinkAPI(publicLid, medium, sharedurl, shareListSenderName),
    });
  } else {
    var urlencoded = new URLSearchParams();
    urlencoded.append('regid', swymConfig.regid);
    urlencoded.append('sessionid', swymConfig.sessionid);
    urlencoded.append('lid', publicLid);
    urlencoded.append('fromname', shareListSenderName);
    urlencoded.append('medium', medium);

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: urlencoded,
      redirect: 'follow',
    };
    return fetch(
      `${SWYM_CONFIG.ENDPOINT}api/v3/lists/reportShare?pid=${encodeURIComponent(
        SWYM_PID,
      )}`,
      requestOptions,
    )
      .then((response) => response.json())
      .then((result) => {
        return result;
      })
      .catch((error) => {
        console.log('error', error);

        return error;
      });
  }
};


Step 2: Add a component to render a wishlist product tile

📘

Code Overview:

wishlistItem(): Main function that renders a wishlisted product tile.

import { AddToCartButton, ProductOptionsProvider } from '@shopify/hydrogen';
import './wishlistItem.css';

export default function wishlistItem({ productId, variantId, product, readOnly = true , onRemoveItem }){
    return (
        <div className='swym-hl-listitem'>
            <div className='swym-hl-wishlist-item-content'>
                <a className='' aria-label={product['dt']} href={product.cprops?.ou} >
                    <img
                        className="w-full"
                        alt={product['dt'] || 'Product Image'}
                        src={product['iu']}
                    />
                    <div className="swym-hl-list-item-title">{product['dt']}</div>
                    <div className='swym-hl-list-item-vendor'>{product['bt']}</div>
                    <div className="swym-hl-list-item-price">${product['pr']}</div>
                </a>
                <ProductOptionsProvider
                    data={{
                    id: `gid://shopify/Product/${productId}`,
                    title: product.dt,
                    vendor: product.bt,
                    variants: [
                        {
                        id: `gid://shopify/ProductVariant/${variantId}`,
                        },
                    ],
                    }}
                >
                    <AddToCartButton className='swym-hl-addtocart-btn swym-hl-bg-color swym-hl-text-color' variantId={`gid://shopify/ProductVariant/${variantId}`} >Add To Cart</AddToCartButton>
                </ProductOptionsProvider>
            </div>
            {!readOnly && (
                <div className='swym-hl-listitem-delete-btn' onClick={onRemoveItem}>
                    <svg
                        xmlns="http://www.w3.org/2000/svg"
                        xmlSpace="preserve"
                        width={12}
                        height={12}
                        viewBox="0 0 512 512"
                    >
                        <path fill="currentColor" d="M443.6 387.1 312.4 255.4l131.5-130c5.4-5.4 5.4-14.2 0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4-3.7 0-7.2 1.5-9.8 4L256 197.8 124.9 68.3c-2.6-2.6-6.1-4-9.8-4-3.7 0-7.2 1.5-9.8 4L68 105.9c-5.4 5.4-5.4 14.2 0 19.6l131.5 130L68.4 387.1c-2.6 2.6-4.1 6.1-4.1 9.8 0 3.7 1.4 7.2 4.1 9.8l37.4 37.6c2.7 2.7 6.2 4.1 9.8 4.1 3.5 0 7.1-1.3 9.8-4.1L256 313.1l130.7 131.1c2.7 2.7 6.2 4.1 9.8 4.1 3.5 0 7.1-1.3 9.8-4.1l37.4-37.6c2.6-2.6 4.1-6.1 4.1-9.8-.1-3.6-1.6-7.1-4.2-9.7z" />
                    </svg>
                </div>
            )}
        </div>
    )
}
.swym-hl-listitem{
    width: 24%;
    position: relative;
}


.swym-hl-wishlist-item-content{
    display: flex; 
    overflow: hidden; 
    flex-direction: column; 
    flex: 1 1 0%; 
    justify-content: space-between; 
    border-radius: 0.25rem; 
    height: 100%;
    padding: 0.5rem; 
    border-width: 1px; 
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); 
}

.swym-hl-listitem-delete-btn{
    position: absolute;
    top: 5px;
    right: 5px;
    padding: 8px;
    cursor: pointer;
    color: gray;
    background: white;
    border-radius: 50%;
    box-shadow: 0 0.5px 0.7px 0.5px lightgray;
}

.swym-hl-list-item-title{
    font-size: 16px;
    margin-top: 5px;
    margin-bottom: 5px;
    font-weight: bold;
}

.swym-hl-list-item-vendor{
    font-size: 14px;
    margin-bottom: 10px;
}

.swym-hl-list-item-price{
    font-size: 14px;
    margin-bottom: 15px;
    font-weight: bold;
}

.swym-hl-addtocart-btn{
    padding: 1rem; 
    border-radius: 0.25rem; 
    width: 100%; 
}

@media only screen and (min-width: 600px) and (max-width: 900px) {
    .swym-hl-listitem{
        width: 32%;
    }
}

@media screen and (max-width: 600px) {
    .swym-hl-listitem{
        width: 48%;
    }
}

.swym-hl-listitem:hover{
    transform: scale(1.05);
    transition: transform 0.1s ease-in-out;
}

Step 3: Component to render the share wishlist modal.

📘

Code Overview:

callMarkListPublicAPI(): Function that calls the markPublic API to mark a list public in order to allow sharing.

callShareViaEmail(): Function to handle sharing via email.

callCopyLink(): Function that creates a copy link to allow wishlist sharing via link.

onChangeEmail(): Function to handle email input change and validation on the share form.

import './shareListPopup.css';

import {useEffect, useState, useContext} from 'react';

import STRINGS from '../../../swym/Utils/strings';
import {
  callFetchPublicListAPI,
  callShareEmailAPI,
  callCopyLinkAPI,
} from '../../../swym/api/wishlistPage-apis';
import {DataContext} from '../wishlist-context';
import {
  getFullUserName,
  getSharedURL,
  validateEmail,
} from '../../../swym/Utils/utilsFunction';
import SwymAlert from '../common/Alert';
import SWYM_CONFIG from '../../../swym/swym.config';

export function classNames(...args) {
  return args.filter(Boolean).join(' ');
}

/*
  @author: swym
  @notice: a modal that pops up when clicking the 'share wishlist' button
  @dev:    handles wishlist sharing - share wishlist via email and copying link
  @param:  onPopupToggle fn
  @param:  lid(list id) string
*/

export function ShareWishlist({onPopupToggle, lid}) {
  const [senderName, setSenderName] = useState(''); // State for sender name
  const [emailValue, setEmailValue] = useState(''); // State for email input value
  const [publicLid, setPublicLid] = useState(''); // State for public list ID
  const [showAlertBox, setshowAlertBox] = useState(false); // State for showing alert box
  const [alertBox, setalertBox] = useState({ type: 'success', title: '', info: '', image: '' }); // State for alert box content
  const [shareListLoading, setShareListLoading] = useState(false); // State for loading status
  const [emailError, setEmailError] = useState(false); // State for email error
  const [buttonDisabled, setButtonDisabled] = useState(true); // State for disabling button

  const {
    setShareListSenderName,
    setSenderEmail,
    shareListSenderName,
    senderEmail,
  } = useContext(DataContext);
  
  let hostName = window.location.host;
  let medium = SWYM_CONFIG.swymSharedMediumCopyLink;
  let sharedurl = getSharedURL(hostName, publicLid);
  
  // Effect to call API on component mount or lid change
  useEffect(() => {
    callMarkListPublicAPI();
  }, [lid]);


  const callMarkListPublicAPI = () => {
    callFetchPublicListAPI(lid).then((response) => {
      setPublicLid(response?.lid);
      setShareListSenderName(
        getFullUserName(response?.userinfo?.fname, response?.userinfo?.lname),
      );
      setSenderEmail(response?.userinfo?.em);
    });
  };

  const callShareViaEmail = (e) => {
    e.preventDefault();
    setShareListLoading(true);
    callShareEmailAPI(publicLid, senderName, emailValue)
      .then((response) => {
        if (response?.errors) {
          setshowAlertBox(true);
          setalertBox({ type:'error', title: 'Error', info: 'Email Not Sent' });
        } else {
          setshowAlertBox(true);
          setalertBox({ type:'success', title: 'Success!', info: 'Email Sent Successfully' });
        }
      })
      .catch((e) => {
        setshowAlertBox(true);
        setalertBox({ type:'error', title: 'Error', info: 'Email Not Sent' });
      })
      .finally(() => {
        setShareListLoading(false);
      });
  };

  const callCopyLink = (e) => {
    e.preventDefault();
    callCopyLinkAPI(publicLid, medium, sharedurl, shareListSenderName).then(
      (response) => {
        let sharedurl = getSharedURL(hostName, response?.lid);
        navigator.clipboard
          .writeText(sharedurl)
          .then(() => {
            setshowAlertBox(true);
            setalertBox({ type:'success', title: 'Success!', info: 'Link Copied' });
          })
          .catch(() => {
            setshowAlertBox(true);
            setalertBox({ type:'error', title: 'Error!', info: 'Link could not be copied' });
          });
      },
    );
  };

  const onChangeEmail = (e) => {
    setEmailValue(e.target.value);
    const isInvalid = validateEmail(
      e.target.value,
      STRINGS.ValidationErrorEmail,
    );
    setEmailError(isInvalid);
    if (isInvalid) {
      setButtonDisabled(true);
    } else {
      setButtonDisabled(false);
    }
  };

  return (
    <div>
      <div id="swym-hl-share-list-popup" className="modal" onClick={(e)=> { e.preventDefault(); e.stopPropagation(); onPopupToggle(false) }}>
            <SwymAlert
                open={showAlertBox}
                toggleAlertState={setshowAlertBox}
                title={alertBox.title}
                info={alertBox.info}
                type={alertBox.type}
            />
            <div className="swym-hl-share-modal-content" onClick={(e)=>{e.preventDefault(); e.stopPropagation();}}>
                <span className="swym-hl-share-modal-close-btn" onClick={()=>onPopupToggle(false)}>&times;</span>
                <div className="swym-hl-share-heading">
                    <h3>Share Wishlist</h3>
                </div>
                <div className="swym-hl-share-body">
                    <div className="swym-hl-share-content">
                        <label className="swym-input-label">
                            {STRINGS.ShareWishlistSenderName}
                        </label>
                        <div className="swym-input-label">
                            <input
                            type="text"
                            placeholder={STRINGS.ShareWishlistNamePlaceholder}
                            id="swym-name"
                            value={senderName}
                            onChange={(e) => setSenderName(e.target.value)}
                            className="swym-share-wishlist-email swym-input swym-no-zoom-fix swym-input-1"
                            />
                        </div>

                        <label className="swym-input-label">
                            {STRINGS.ShareWishlistRecipientsEmail}
                        </label>
                        <div className="swym-input-label">
                            <input
                            type="text"
                            placeholder={STRINGS.ShareWishlistEmailPlaceholder}
                            id="swym-email"
                            value={emailValue}
                            onChange={(e) => onChangeEmail(e)}
                            className="swym-share-wishlist-name swym-input swym-no-zoom-fix swym-input-1"
                            />
                            {emailError && (
                            <span className="error-msg" role="alert">
                                {emailError}
                            </span>
                            )}
                        </div>
                    </div>
                    <div className='swym-hl-share-modal-action'>
                        <button className='swym-hl-bg-color swym-hl-share-modal-action-btn swym-hl-text-color' onClick={(e) => callShareViaEmail(e)}>{STRINGS.ShareWishlistEmailCTA} </button>
                        <button className='swym-hl-bg-outline swym-hl-share-modal-action-btn' onClick={(e) => callCopyLink(e)} >
                            <span style={{fontSize: '16px'}} > {STRINGS.ShareWishlistAlternateShareLabel}:</span>
                            {STRINGS.ShareWishlistCopyLinkText}
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
  );
}


#swym-hl-share-list-popup{
    position: fixed; 
    top: 0;
    right: 0;
    bottom: 0;
    left: 0; 
    z-index: 10000; 
    display: flex;
    justify-content: center; 
    align-items: center; 
    width: 100vw; 
    height: 100vh;
    background-color: #00000061;
}

.swym-hl-share-modal-content {
    margin: auto;
    padding: 20px;
    border: 1px solid #888;
    display: flex; 
    flex-direction: column; 
    color: #000000; 
    background-color: #ffffff; 
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); 
    width: 600px;
    position: relative;
}
  
.swym-hl-share-modal-close-btn{
    padding: 4px;
    position: absolute;
    top: 5px;
    right: 15px;
    font-size: 25px;
    z-index: 100;
    cursor: pointer;
}

.swym-hl-share-heading {
    display: flex;
    position: relative;
    box-sizing: border-box;
    border-bottom: 1px solid #828282;
    padding-left: 0px;
    padding-top: 0px;
    padding-bottom: 10px;
}

.swym-hl-share-content {
    margin-top: 20px;
}

.swym-hl-share-modal-loader{
    display: flex;
    justify-content: center;
    align-item: center;
    margin-right: 5px;
}

.swym-hl-share-modal-action-btn{
    cursor: pointer;
    padding: 8px 26px;
    margin-top: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 170px;
}

.swym-hl-share-modal-action {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
}

@media screen and (max-width: 450px) {
    .swym-hl-share-modal-action-btn{
        width: 100%;
    }
}

.swym-input-label {
    font-size: 12px;
    line-height: 14px;
    letter-spacing: 0.05em;
    color: #828282;
    margin-bottom: 5px;
}

.swym-share-wishlist-email,
.swym-share-wishlist-name {
    font-size: 14px;
    letter-spacing: 0.05em;
    width: 100%;
}

.swym-input-label{
    padding-bottom: 20px;
}

.swym-input-has-error {
    border: 1px solid #CC0000 !important;
}

.error-msg {
    font-size: 12px;
    line-height: 14px;
    letter-spacing: 0.05em;
    color: #CC0000;
    font-style: italic;
    padding: 5px;
}

.swym-title-new {
    padding-top: 0px;
    padding-left: 0px;
}

Step 4: Add components for the Wishlist page

📘

Code Overview:

fetchList, fetchListWithContents, fetchSwymAccessToken: These are wrapper functions that call Swym APIs. They can be added from the 'Prepare Client-side APIs' guide.

callDeleteProductFromWishlist: This component is available in step 1 of this guide.

SwymAlert, WishlistContext: This is a component that triggers a success notification after a product has been added/removed from a list. They are present in the components file shared in the Pre-requisites section of the Prepare Server-side APIs... guide.

ShareWishlist: This component is used to render a share wishlist form. The code for this component is found in step 4 of this guide.

setSwymLocalStorageListData: This component saves wishlisted items in localstorage for quicker loading and is found in the utilils folder shared in the Pre-requisites section of the Prepare Server-side APIs... guide.

WishlistItem: This component is used to render a wishlist product tile. The code for this component is found in step 3 of this guide.

EmptyWishlist(): Function to show an empty wishlist view.

WishlistPage(): Main function that renders the Wishlist page.

getSetListItems(): Function to fetch and set items for the selected list

removeFromList(): Function to remove an item from the list state.

removeItemFromWishlist(): Function to remove an item from the wishlist and show an alert.

onShareWishlistClick(): Function to handle share wishlist click.

getSwymAccessToken(): Function to fetch Swym access token.

handleListValueChange(): Function to handle list value change in dropdown.

toggleMenu(): Function to toggle dropdown menu open/close.

wishlistPage.css: The code for styling can be found at the bottom of this page.

import './wishlistPage.css';
import {useEffect, useState} from 'react';
import {callDeleteProductFromWishlist} from '../../../swym/api/wishlistPage-apis';
import {
  fetchList,
  fetchListWithContents,
  fetchSwymAccessToken,
} from '../../../swym/api/store-apis';
import SwymAlert from '../common/Alert';
import {ShareWishlist} from '../shareListPopup/shareListPopup.client';
import WishlistContext from '../wishlist-context';
import { setSwymLocalStorageListData } from '../../../swym/Utils/Utils';
import WishlistItem from '../wishlistItem/wishlistItem.client';

export function EmptyWishlist(){
  return (
    <div style={{display: 'block', textAlign: 'center'}}>
      <br />
      <h3 className="swym-hl-empty-wl-title">Love It? Add To My Wishlist</h3>
      <br />
      <p>
        My Wishlist allows you to keep track of all of your favorites and shopping activity whether you're on your computer, phone, or tablet.
      </p>
      <p className='mt-4'>
        You won't have to waste time searching all over again for that item you loved on your phone the other day - it's all here in one place!
      </p>
      <br />
      <button className='swym-hl-continue-shop-btn swym-hl-bg-color swym-hl-text-color' onClick={()=>{
        window.location.href = '/products'
      }}>Continue Shopping</button>
    </div>
  )
}

/*
  @author:  swym
  @notice: Page on which user can see all the wishlisted products
  @dev:    render withslited products. user can add products to cart or remove products from wishlist
  @param:  it will use regid stored in localstorage to fetch user related activities
*/

export function WishlistPage() {
  const [list, setItems] = useState();

  const [showAlertBox, setshowAlertBox] = useState(false);
  const [alertBox, setalertBox ] = useState({ type: 'success', title:'', info: '', image: null });

  const [selectedListIndex, setselectedListIndex] = useState(0);
  const [showLoading, setshowLoading] = useState(true);
  const [wishlistCreatedLists, setWishlistCreatedLists] = useState([]);
  const [openShareModal, setOpenShareModal] = useState(false);
  const [swymAccessToken, setSwymAccessToken] = useState(false);
  const [variantDeleted, setVariantDeleted] = useState(false);
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);

  useEffect(() => {
    getSwymAccessToken();
  }, [swymAccessToken]);

  useEffect(() => {
    fetchList()
      .then((response) => {
        setWishlistCreatedLists(response);
        setSwymLocalStorageListData(response);
        setshowLoading(false);
      })
      .catch((e) => console.log(e));
  }, [variantDeleted]);

  useEffect(() => {
    if (wishlistCreatedLists && wishlistCreatedLists.length > 0) {
      getSetListItems();
    }
  }, [wishlistCreatedLists, selectedListIndex]);

  const getSetListItems = () => {
    fetchListWithContents(wishlistCreatedLists[selectedListIndex]?.lid)
      .then((response) => {
        setItems(response?.items);
      })
      .catch((e) => console.log(e));
  };

  const removeFromList = (empi, epi) => {
    if (list) {
      let listArray = list.filter(
        (listItem) => empi !== listItem.empi && epi !== listItem.epi,
      );
      setItems(listArray);
    }
  };

  const removeItemFromWishlist = (productData, showDeleteAlert) => {
    callDeleteProductFromWishlist(productData)
      .then((response) => {
        setVariantDeleted((prev) => !prev);
        removeFromList(productData.empi, productData.epi);
        if (showDeleteAlert) {
          setshowAlertBox(true);
          setalertBox({ type:'success', title: 'Success', info: `Product removed from wishlist`, image: productData.iu });
        }
      })
      .catch((e) => {
        console.log(e);
        setshowAlertBox(true);
        setalertBox({ type:'error', title: 'Error', info: `Product not removed from wishlist`, image: productData.iu });
      });
  };

  const onShareWishlistClick = (event) => {
    event.preventDefault();
    setOpenShareModal(true);
  };

  const getSwymAccessToken = () => {
    fetchSwymAccessToken()
      .then((response) => {
        if (response && response !== '') {
          setSwymAccessToken(true);
        } else {
          setSwymAccessToken(false);
        }
      })
      .catch((e) => {
        console.log(e);
      });
  };


  const handleListValueChange = (e, index) => {
    setselectedListIndex(index);
    e.preventDefault();
    setIsDropdownOpen(false);
  };

  const toggleMenu = () => {
    setIsDropdownOpen(!isDropdownOpen);
  };

  return (
    <div>
      <WishlistContext>
        <SwymAlert
          open={showAlertBox}
          toggleAlertState={setshowAlertBox}
          title={alertBox.title}
          info={alertBox.info}
          type={alertBox.type}
          image={alertBox.image}
        />
        {openShareModal && (
          <ShareWishlist
            onPopupToggle={setOpenShareModal}
            lid={wishlistCreatedLists[selectedListIndex]?.lid}
          />
        )}
        <div className="swym-hl-wishlist-page">
          <div className='swym-hl-wishlist-page-title'>
            Wishlist
          </div>
          <br />
          {showLoading && (
            <div className='swym-hl-wishlist-loader-container'>
              <div className='swym-hl-wishlist-loader' style={{ width: '100%', display: 'flex', justifyContent: 'center', padding: '5rem' }}>
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="50"><radialGradient id="a12" cx=".66" fx=".66" cy=".3125" fy=".3125" gradientTransform="scale(1.5)"><stop offset="0" stopColor="currentColor"></stop><stop offset=".3" stopColor="currentColor" stopOpacity=".9"></stop><stop offset=".6" stopColor="currentColor" stopOpacity=".6"></stop><stop offset=".8" stopColor="currentColor" stopOpacity=".3"></stop><stop offset="1" stopColor="currentColor" stopOpacity="0"></stop></radialGradient><circle transform-origin="center" fill="none" stroke="url(#a12)" strokeWidth="15" strokeLinecap="round" strokeDasharray="200 1000" strokeDashoffset="0" cx="100" cy="100" r="70"><animateTransform type="rotate" attributeName="transform" calcMode="spline" dur="2" values="360;0" keyTimes="0;1" keySplines="0 0 1 1" repeatCount="indefinite"></animateTransform></circle><circle transform-origin="center" fill="none" opacity=".2" stroke="currentColor" strokeWidth="15" strokeLinecap="round" cx="100" cy="100" r="70"></circle></svg>
              </div>
            </div>
          )}
          {!showLoading && (
            <>
              {( !wishlistCreatedLists || wishlistCreatedLists.length == 0 ) && (
                <EmptyWishlist />
              )}
              <div>
                {wishlistCreatedLists && wishlistCreatedLists.length > 1 && (
                  <div>
                      <div>
                        <div className="swym-hl-wl-dropdown-container">
                          <div>
                            <button  onClick={toggleMenu} className="swym-hl-wl-list-dropdown-btn">
                              {wishlistCreatedLists &&
                                wishlistCreatedLists.length > 0 &&
                                wishlistCreatedLists[selectedListIndex].lname}
                              {!wishlistCreatedLists ||
                                (wishlistCreatedLists.length == 0 &&
                                  'No list found')}
                              <svg
                                className="swym-hl-dropdown-icon"
                                xmlns="http://www.w3.org/2000/svg"
                                viewBox="0 0 20 20"
                                fill="currentColor"
                                aria-hidden="true"
                              >
                                <path
                                  fillRule="evenodd"
                                  d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
                                  clipRule="evenodd"
                                />
                              </svg>
                            </button>
                          </div>
                          {isDropdownOpen && (
                            <div className="swym-hl-wl-dropdown-list">
                                {wishlistCreatedLists &&
                                  wishlistCreatedLists.length > 0 &&
                                  wishlistCreatedLists.map(({lname}, index) => (
                                    <a
                                      onClick={(e) =>
                                        handleListValueChange(e, index)
                                      }
                                      key={`menu_${index}`}
                                      value={selectedListIndex}
                                      href="#"
                                      className={`swym-hl-wl-dropdown-list-item ${(selectedListIndex == index)?'swym-hl-dropdown-list-item-active':''}`}
                                    >
                                      {lname}
                                    </a>
                                  ))}
                            </div>
                          )}
                        </div>
                        <br />
                        <br />
                      </div>
                  </div>
                )}
                <div>
                  <div className="swym-hl-wl-content-header">
                    <h2 className="swym-hl-wl-selected-lname">
                      {wishlistCreatedLists &&
                        wishlistCreatedLists.length > 0 &&
                        wishlistCreatedLists[selectedListIndex]?.lname}{' '}
                      {wishlistCreatedLists &&
                        wishlistCreatedLists.length > 0 &&
                        ` (${wishlistCreatedLists[selectedListIndex]?.listcontents?.length})`}
                    </h2>
                    {wishlistCreatedLists &&
                      wishlistCreatedLists.length > 0 &&
                      swymAccessToken && (
                        <div
                          className="swym-hl-wishlist-page-share-btn"
                          onClick={(event) => onShareWishlistClick(event)}
                        >
                          <svg
                            xmlns="http://www.w3.org/2000/svg"
                            xmlSpace="preserve"
                            width={15}
                            height={15}
                            fill="#035587"
                            viewBox="0 0 458.624 458.624"
                          >
                            <path d="M339.588 314.529a71.683 71.683 0 0 0-38.621 11.239l-112.682-78.67a72.036 72.036 0 0 0 2.798-19.871c0-6.896-.989-13.557-2.798-19.871l109.64-76.547c11.764 8.356 26.133 13.286 41.662 13.286 39.79 0 72.047-32.257 72.047-72.047S379.378 0 339.588 0c-39.79 0-72.047 32.257-72.047 72.047 0 5.255.578 10.373 1.646 15.308l-112.424 78.491c-10.974-6.759-23.892-10.666-37.727-10.666-39.79 0-72.047 32.257-72.047 72.047s32.256 72.047 72.047 72.047c13.834 0 26.753-3.907 37.727-10.666l113.292 79.097a72.108 72.108 0 0 0-2.514 18.872c0 39.79 32.257 72.047 72.047 72.047s72.047-32.257 72.047-72.047-32.257-72.048-72.047-72.048z" />
                          </svg>
                        </div>
                      )}
                  </div>
                  <br />
                  <div className='swym-hl-wishlist-page-list-container'>
                    {list &&
                      list.length > 0 &&
                      list.map((item) => (
                        <WishlistItem key={item.epi} productId={item.empi} variantId={item.epi} title={item.dt} product={item} readOnly={false} onRemoveItem={()=>removeItemFromWishlist(item, true)} />
                      ))}
                  </div>
                </div>
              </div>
            </>
          )}
        </div>
      </WishlistContext>
    </div>
  );
}

.swym-hl-wl-dropdown-container{
  display: inline-block; 
  position: relative; 
  text-align: left; 
}

.swym-hl-wl-list-dropdown-btn{
  display: inline-flex; 
  padding-top: 0.5rem;
  padding-bottom: 0.5rem; 
  padding-left: 1rem;
  padding-right: 1rem; 
  justify-content: center; 
  border-width: 1px; 
  border-color: #D1D5DB; 
  width: 100%; 
  font-size: 0.875rem;
  line-height: 1.25rem; 
  font-weight: 500; 
  color: #374151; 
  background-color: #ffffff; 
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); 
}

.swym-hl-dropdown-icon{
  margin-left: 0.5rem; 
  margin-right: -0.25rem; 
  width: 1.25rem; 
  height: 1.25rem; 
}

.swym-hl-wl-dropdown-list{
  position: absolute; 
  left: 0; 
  z-index: 10; 
  margin-top: 0.5rem; 
  //box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 
  --ring-color: #000000; 
  --ring-opacity: 0.05; 
  width: 14rem; 
  background-color: #ffffff; 
  transform-origin: top right; 
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); 
  border: 1px solid lightgray;
  border-radius: 4px;
}

.swym-hl-wl-dropdown-list-item{
  display: block; 
  padding-top: 0.5rem;
  padding-bottom: 0.5rem; 
  padding-left: 1rem;
  padding-right: 1rem; 
  font-size: 0.875rem;
  line-height: 1.25rem; 
  color: #374151; 
}

.swym-hl-wl-dropdown-list-item.swym-hl-dropdown-list-item-active{
  color: #111827; 
  background-color: #F3F4F6; 
}

.swym-hl-wl-dropdown-list-item:hover{
  color: #111827; 
  background-color: #F3F4F6; 
}

.swym-hl-wl-list-dropdown-btn:hover{
  background-color: #F9FAFB; 
}

.swym-hl-wishlist-page-title{
  font-size: 20px;
  font-weight: bold;
}

.swym-hl-wl-content-header{
  display: flex;
}

.swym-hl-wl-selected-lname{
  flex: 1 1 0%; 
  font-size: 1.25rem;
  line-height: 1.75rem; 
  font-weight: 700; 
}

.swym-hl-wishlist-page-share-btn{
  padding: 10px; 
  border-radius: 9999px; 
  border-width: 1px; 
  cursor: pointer; 
}

.swym-hl-wishlist-page-list-container {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.swym-list-style {
    padding: 10px 20px;
    padding-bottom: 0px;
    cursor: pointer;
}

.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  padding: 12px 16px;
  z-index: 1;
  margin: 10px;
  margin-left: 0px
}

.swym-wishlist-style-ul{
  margin:20px 0;
  list-style:none;
}

.swym-wishlist-style-li{
  position:relative;
  padding:15px 10px;
  border-bottom:1px solid rgba(255,255,255,.1);
  overflow:hidden;
  margin-right: 40px;
  cursor: pointer;
}

.swym-wishlist-style-li:before{
  content:'';
  position:absolute;
  left:-100%;
  top:0;
  width:100%;
  height:100%;
  background:#434655;
  z-index:-1;
}

.swym-wishlist-style-li:hover:before{
  left:0;
  border-right:5px solid #434655;
  opacity: 0.5;
}

.swym-wishlist-style-li a.active {
  background-color: yellow;
}


 .swym-wishlist-badge {
  width: 30px;
  height: 30px;
  border-radius: 30px;
  display: inline-block;
  overflow: hidden;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 14px;
  flex-shrink: 0;
}

.selected {
  background-color:#035587;
  font-weight: bold;
  font-size: 16px;
  
}

.add-button{
  background-color:#5a31f4;
}

.swym-spinner{
  width:40px;
  height: 40px;
  border: 2px solid black;
  border-radius: 25%;
  animation: rotator 1.4s linear infinite;
}
  
@keyframes rotator {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(270deg);
  }
}

.grid-item-view:hover{
  transform: scale(1.05);
  transition: transform 0.1s ease-in-out;
}

.swym-hl-continue-shop-btn{
  padding: 0.5rem 1rem;
  margin-top: 1rem;
  border-radius: 0.25rem;
  font-weight: bold;
}

.swym-hl-continue-shop-btn:hover{
  transform: scale(1.05);
  transition: transform 0.1s ease-in-out;
}


.swym-hl-empty-wl-title{
  font-size: 1.25rem;
  line-height: 1.75rem; 
  font-weight: 700; 
}

Demo

Swym Demo Store