Bulk add to cart

Hello everyone, i’m new with code :slight_smile:
I’m trying to replicate this: https://www.wix.com/velo/example/bulk-add-to-cart

But i want that when i click on “bracelet charms”, the position of the charm is always the same.
Is that possible? (In this example the position of the charm changes according to the order in which it is clicked and to free slot)

CODE EXAMPLE

//-------------Imports-------------//

// Import the wix-data module for working with queries.
import wixData from 'wix-data';

//-------------Global Variables-------------//

// Maximum number of charms that can be added to a bracelet.
const MAX_NUMBER_OF_CHARMS = 5;
// List of selected charms.
const selectedCharms = [];
// Map of bracelet product information.
let braceletsMap = {};
// Selected bracelet.
let selectedBracelet;

//-------------Page Setup-------------//

// Retrieve data when the page loads. 
$w.onReady(async () => {
    // Set a list of bracelet material types.
    const braceletNames = ['Silver Bracelet', 'Rose Gold Bracelet', 'Gold Bracelet'];
    // Query the collection for products whose name is one of the names from the bracelet names.
    let newQuery = await wixData.query("Stores/Products").hasSome("name", braceletNames).find();
    // Get the items from the query results.
    const allBracelets = newQuery.items;
    // Create the bracelet map based on the items returned by the query.
    // For each item returned:
    allBracelets.forEach(product => {
        // Set the map key to the first part of the name (e.g. 'Silver Bracelet' becomes 'silver'). 
        const keyName = product.name.split(' ')[0].toLowerCase();
        // Set the corresponding map value to the product data.
        braceletsMap[keyName] = product;
    });
});

//-------------Repeater Setup-------------//

// Set up each item in the charms repeater as it is loaded.
export function charmsRepeater_itemReady($w, itemData, index) {
    // Set the action that occurs when a user clicks the current charm image.
    $w('#charmImage').onClick(() => {
        // If the border is hidden, meaning the charm is being selected:
        if ($w('#charmBorder').hidden) {
            // Add the charm to the list of selected charms.
            selectedCharms.push(itemData);
            // Show the charm border, indicating it is now selected.
            $w('#charmBorder').show();
            // Add the selected charm to the bracelet image. 
            showCharmOnBracelet(itemData.mainMedia);
            // Set the selected charms repeater in step 3 to reflect the charms that have been selected.
            $w('#selectedCharmsRepeater').data = selectedCharms;
            // If the user has now selected the maximum number of charms:
            if (selectedCharms.length === MAX_NUMBER_OF_CHARMS) {
                // Disable further charm selection.
                disableCharmsSelection();
            }
        // If the border is not hidden, meaning the charm is being deselected:
        } else {
            // If further charm selection was disabled because the user had selected the maximum number of charms:
            if (selectedCharms.length === MAX_NUMBER_OF_CHARMS) {
                // Enable charm selection because an empty slot has opened up.
                enableCharmsSelection();
            }
            // Find the index of the charm that is being deselected in the list of selected charms.
            const indexOfCharm = selectedCharms.findIndex(item => item._id === itemData._id);
            // Remove the charm that is being deselected from the list of selected charms.
            selectedCharms.splice(indexOfCharm, 1);
            // Set the selected charms repeater in step 3 to reflect the charms that are still selected.
            $w('#selectedCharmsRepeater').data = selectedCharms;
            // Remove deselected charm from the bracelet image. 
            hideCharmFromBracelet(itemData.mainMedia);
            // Hide the charm border, indicating it is no longer selected. 
            $w('#charmBorder').hide();
        }
    });
}

// Set up each item in the selected charms repeater as it is loaded.
export function selectedCharmsRepeater_itemReady($w, itemData, index) {
    // Set the current item's image to be the image from current item's data.
    $w('#selectedCharmImage').src = itemData.mainMedia;
}

//-------------Charm Selection Logic-------------//

// Shows an overlay on top of each charm that is not selected, indicating that those charms are disabled for selection. 
function disableCharmsSelection() {
    // For each charm in the charms repeater:
    $w('#charmsRepeater').forEachItem(($w, itemData) => {
        // If it isn't already selected.
        if (!isCharmSelected(itemData)) {
            // Indicate that it is disabled by showing the overlay.
            $w('#charmOverlay').show();
        }
    });
}

// Hides an overlay from on top of each charm, indicating that they are all enabled for selection.
function enableCharmsSelection() {
    // For each charm in the charms repeater:
    $w('#charmsRepeater').forEachItem($w => {
        // Indicate that it is enabled by hiding the overlay.
        $w('#charmOverlay').hide();
    });
}

// Checks to see if the specified charm has already been selected.
function isCharmSelected(charmItem) {
    return selectedCharms.findIndex(item => item._id === charmItem._id) > -1;
}

//-------------Charm Show/Hide Logic-------------//

// Show the specified charm in the first available charm slot on the bracelet.
function showCharmOnBracelet(charmImage) {
    // For each charm slot in the bracelet:
    for (let i = 1; i <= 5; i++) {
        // If the charm image in the current slot is hidden, meaning it is an open slot:
        if ($w(`#charmPlace${i}`).hidden) {
            // Set the image for the open slot to the specified charm image.
            $w(`#charmPlace${i}`).src = charmImage;
            // Show the charm image.
            $w(`#charmPlace${i}`).show();
            // End the loop.
            break;
        }
    }
}

// Hide the specified charm from the slot it currently occupies on the bracelet.
function hideCharmFromBracelet(charmImage) {
    // For each charm slot in the bracelet:
    for (let i = 1; i <= 5; i++) {
        // If the charm image in the current slot is the same as the specified image:
        if ($w(`#charmPlace${i}`).src === charmImage) {
            // Set the image for the slot to an empty image.
            $w(`#charmPlace${i}`).src = 'http://';
            // Hide the charm image.
            $w(`#charmPlace${i}`).hide();
            // End the loop.
            break;
        }
    }
}

//-------------Material Selection-------------//

// Select the gold bracelet using the "gold" button.
export function gold_click(event, $w) {
    // Call the selectBracelet() function with the data from the braceletsMap that corresponds to the gold bracelet.
    selectBracelet(braceletsMap.gold);
}

// Select the silver bracelet using the "silver" button.
export function silver_click(event, $w) {
    // Call the selectBracelet() function with the data from the braceletsMap that corresponds to the silver bracelet.
    selectBracelet(braceletsMap.silver);
}

// Select the rose gold bracelet using the "rose gold" button.
export function rosegold_click(event, $w) {
    // Call the selectBracelet() function with the data from the braceletsMap that corresponds to the rose gold bracelet.
    selectBracelet(braceletsMap.rose);
}

// Select the specified bracelet.
function selectBracelet(bracelet) {
    // Set the global selectedBracelet variable to be used elsewhere.
    selectedBracelet = bracelet;
    // Set the bracelet image to the image found in the specified bracelet's product data.
    $w('#bracelet').src = bracelet.mainMedia;
    // Enable the button that takes users to the next step.
    $w('#nextButton').enable();
    // Enable the clickable text that takes users to the next step.
    $w('#chooseCharmsText').enable();
    // Set the value of the selected bracelet text element in step 3.
    $w('#selectedBracelet').text = bracelet.name + " + these charms:";
    // Show the selected bracelet text element in step 3.
    $w('#selectedBracelet').show();
}

//-------------Step Navigation-------------//

// Move to step 1 using the step's header text.
export function selectBraceletText_click(event, $w) {
    // Expand step 1 container.
    $w('#bracelectSelectionStrip').expand();
    // Collapse step 2 container.
    $w('#charmsSelectionStrip').collapse();
    // Collapse step 3 container.
    $w('#addToCartStrip').collapse();
    
}

// Move to step 2 using the step's header text.
export function chooseCharmsText_click(event, $w) {
    // Collapse step 1 container.
    $w('#bracelectSelectionStrip').collapse();
    // Expand step 2 container.
    $w('#charmsSelectionStrip').expand();
    // Collapse step 3 container.
    $w('#addToCartStrip').collapse();
}

// Move to step 3 using the step's header text.
export function reviewBraceletText_click(event, $w) {
    // Collapse step 1 container.
    $w('#bracelectSelectionStrip').collapse();
    // Collapse step 2 container.
    $w('#charmsSelectionStrip').collapse();
    // Expand step 3 container.
    $w('#addToCartStrip').expand();
}

// Move to step 2 using the "Next" button.
export function nextButton_click(event, $w) {
    // Collapse step 1 container.
    $w('#bracelectSelectionStrip').collapse();
    // Expand step 2 container.
    $w('#charmsSelectionStrip').expand();
    // Enable step 2 header text.
    $w('#chooseCharmsText').enable();
}

// Move to step 3 using the "Finish" button.
export function finishButton_click(event, $w) {
    // Collapse step 1 container.
    $w('#bracelectSelectionStrip').collapse();
    // Collapse step 2 container.
    $w('#charmsSelectionStrip').collapse();
    // Call the calcAndShowPrice() function.
    calcAndShowPrice();
    // Expand step 3 container.
    $w('#addToCartStrip').expand();
    // Enable step 3 header text.
    $w('#reviewBraceletText').enable();
    // Enable the "Add to Cart" button.
    $w('#addToCartButton').enable();
}

// Calculate and show the price of the bracelet and selected charms.
function calcAndShowPrice() {
    // Get the price of the selected bracelet.
    let price = selectedBracelet.price;
    // For each charm that has been selected:
    selectedCharms.forEach(charm => {
        // Add the charm's price to the total price.
        price += charm.price;
    });
    // Set the value of the price text element in step 3.
    $w('#price').text = price.toString();
}

//-------------Add to Cart-------------//

// Add the selected bracelet and charms to the shopping cart.
export async function addToCartButton_click(event, $w) {
    // // Add the selected bracelet to the shopping cart.
    let itemsToAdd = [{productID: selectedBracelet._id, quantity: 1, options: {}}];

    // For each selected charm:
    selectedCharms.forEach(function(charm){
        itemsToAdd.push({ productID: charm._id, quantity: 1, options:{} });
    });
    
    // add all selected items at one time
    let res = await $w('#shoppingCartIcon').addProductsToCart(itemsToAdd);
}
1 Like

This is an interesting project, thanks.

Within the cart all of the charms and the bracelet are listed as seperate items, which are great for this project.

We are designing a custom tshirt design dynamic page. There are eight options. We would like the selected option to appear like they do on the product custom page like this example:

https://www.wix.com/code-examples/bulk-add-to-cart-vw

We would like the cart page to import the final design with all eight layered product selection image options to become one single image. Is this possible? We are really stuck and need some help please:)