Plz Help! "Wix Gift Quiz" with 1000 products for London's Oldest Art Gallery

Hi all,

Firstly I just wanted to say a huge thank you to everyone who has participated in this forum over the last year or so - it’s been such a help for me and I’ve learnt so much!

I’ve been developing an e-commerce Wix site for an art gallery for the last few months - it’s been a labour of love especially as the gallery is almost 267 years old and so has amassed quite a collection of products - but the finish line is nearly in sight! Once it goes live I’ll share bits of the code which I hope the community will find useful (especially for custom Wix store pages - I’ve seen various unanswered questions over the last few months which I think I’ve solved… but equally if anyone wants to message me privately about how anything works I’m happy to share!)

WEBSITE OVERVIEW…
Rather than create a typical e-commerce shop front with hundreds of products/filters to choose from, I wanted to create a more personal shopping experience to help people discover paintings that are right for them. I then discovered the Wix Gift Quiz tutorial and thought my prayers had been answered… however I’ve hit a couple of snags and I just can’t get my head around it - I’ve read the tutorial back-to-back and searched the forums for answers but I can’t seem to find a resolution, so I hope you can help!

SITE STRUCTURE + ERRORS?
For the gift quiz code itself, I am actually keeping this basically exactly the same - however rather than use the Stores/Products database, I have a custom Products Database due to the complexities of the numerous product variations. I have replicated the same fields/collections used in the tutorial so thought this would work as it does in the demo but I’m getting a few problems, which I’ve listed below… I’m not sure if its due to the volume of products (I hope not) or just something stupid that I’ve missed!!!

I’ve created 3 versions of the same gift quiz to try and identify the problem…

  1. Wix Store Collection + 4 Questions = Incorrect Results
    https://www.toogood.london/perfect-painting-1

  2. Preferred Custom Product Collection + 4 Questions = No Results?
    https://www.toogood.london/perfect-painting-2

  3. Wix Store Collection + 3 Questions = Better results, but not 100% correct
    https://www.toogood.london/perfect-painting-3 (same as #1 but one less question)

I need to get the option 2 working as this database has all the extra product data in it… anyway I’ve copied the code below with a few screenshots. Any help in advanced would be super super much appreciated!

Many thanks,
C

PERFECT PAINTING #1:

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

import wixData from 'wix-data';

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

// List of selected answers.
const selectedAnswers = [];
// List of IDs of all the answer buttons.
const quizAnswersIds = ["wildlife", "townscape", "stilllife", "modern", "marine", "landscape", "figurative",
"oil", "sculpture", "watercolour", 
"small", "medium", "large", "priceone", "pricetwo", "pricethree", "pricefour", "pricefive", "pricesix"
];
// Number of questions plus one for the results.
let numberOfSlides;

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

$w.onReady(() => {
 // Set the global numberOfSlides variable from the slideshow length.
    numberOfSlides = $w('#quizSlides').slides.length;
 
 // For each answer button:
    quizAnswersIds.forEach(answer => {
 // Set the action that occurs when a user clicks the answer button.
        $w(`#${answer}`).onClick(() => {
 // Add the answer to the global selected answers list.
            selectedAnswers.push(answer);
 // Move to the next slide.
            $w('#quizSlides').next();
        })
    })
});

//-------------Quiz Finish-------------//

// Set the action that occurs when a user is moved to the next slide.
export async function quizSlides_change(event, $w) {
 // If the quiz is finished:
 if (isQuizFinished()) {
 // Get the keywords associated with each product by calling the getKeywordsPerProduct() function.
 let quizProducts = await getKeywordsPerProduct();
 // For each selected answer:
        selectedAnswers.forEach(answer => {
 // Filter out the products that do not match the answer.
            quizProducts = filterProductsPerAnswer(quizProducts, answer);
        });
 // Extract the product IDs from the filtered product.
        quizProducts = quizProducts.map(quizProduct => quizProduct.productId);
 // Get a random list from the remaining filtered products.
        quizProducts = getRandomItemsFromArray(quizProducts, numberOfSlides);
 // Populate the recommended products repeater with the product data that corresponds to the randomly selected products.
        $w('#recommendedProducts').data = await getProductsData(quizProducts);
    }
}

// Checks if the quiz has been completed.
function isQuizFinished() {
 // Check if the current slide is the last slide.
 return $w('#quizSlides').currentIndex === numberOfSlides - 1;
}

// Get the keywords associated with each product.
async function getKeywordsPerProduct() {
 // Query the "productsSearch" collection for the product keywords.
 let productsSearch = await wixData.query("productsSearchTest").find();
 // Extract the items from the query results.
    productsSearch = productsSearch.items;
 // Process the returned item data to make it easier to work with.
    productsSearch = productsSearch.map(item => {
 return {
            productId: item.product,
            keywords: item.keywords.split(",")
        }
    });
 // Return the processed item data.
 return productsSearch;
}

// Filter out products that don't match the specified answer. 
function filterProductsPerAnswer(quizProducts, answer) {
 // Use the JavaScript filter() function to filter out products that don't match the specified answer. 
 const filteredProducts = quizProducts.filter(quizProduct => {
 return quizProduct.keywords.includes(answer)
    });
 // Return the filtered product list.
 return filteredProducts;
}

// Get a specified number of random items from the specified array.
function getRandomItemsFromArray(productsArr, numberOfItems){
 // List for storing the randomly selected products.
 const productsIds = [];
 // Number of products in the specified array.
 const numberOfProducts = productsArr.length;

 // For specified the number of items or the number of products that were specified, whichever is lower: 
 for (let i = 0; i < numberOfItems && i < numberOfProducts; i++){
 // Get a random valid index.
 const randomIndex = getRandomInt(0, numberOfProducts -1 );
 // Add the product at that random index to the list of selected products.
        productsIds.push(productsArr[randomIndex]);
 // Remove the already selected product from the list of products to select from.
        productsArr.splice(randomIndex, 1);
    }
 // Return the randomly selected products.
 return productsIds;
}

// Get a random number between two specified values.
function getRandomInt(min, max) {
 // Use JavaScript math functions to get a random number between two specified values.
 return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Get the product data associated with the specified product IDs.
async function getProductsData(productsIds) {
 // Query the "Products" collection for products whose ID is one of the specified IDs.
 const productsData = await wixData.query("Stores/Products")
        .hasSome("_id", productsIds)
        .find();
 // Return the matching products.
 return productsData.items;
}

// Set up each item in the recommended products repeater when it receives data as the quiz is finished.
export function recommendedProducts_itemReady($item, itemData, index) {
 // Populate the elements from the current item's data.
    $item('#name').text = itemData.name;
    $item('#image').src = itemData.mainMedia;
    $item('#image').link = itemData.productPageUrl;
    $item('#price').text = itemData.formattedPrice;
}

PERFECT PAINTING #2:

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

import wixData from 'wix-data';

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

// List of selected answers.
const selectedAnswers = [];
// List of IDs of all the answer buttons.
const quizAnswersIds = ["wildlife", "townscape", "stilllife", "modern", "marine", "landscape", "figurative",
"oil", "sculpture", "watercolour", 
"small", "medium", "large", 
"priceone", "pricetwo", "pricethree", "pricefour", "pricefive", "pricesix"
];
// Number of questions plus one for the results.
let numberOfSlides;

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

$w.onReady(() => {
 // Set the global numberOfSlides variable from the slideshow length.
    numberOfSlides = $w('#quizSlides').slides.length;
 
 // For each answer button:
    quizAnswersIds.forEach(answer => {
 // Set the action that occurs when a user clicks the answer button.
        $w(`#${answer}`).onClick(() => {
 // Add the answer to the global selected answers list.
            selectedAnswers.push(answer);
 // Move to the next slide.
            $w('#quizSlides').next();
        })
    })
});

//-------------Quiz Finish-------------//

// Set the action that occurs when a user is moved to the next slide.
export async function quizSlides_change(event, $w) {
 // If the quiz is finished:
 if (isQuizFinished()) {
 // Get the keywords associated with each product by calling the getKeywordsPerProduct() function.
 let quizProducts = await getKeywordsPerProduct();
 // For each selected answer:
        selectedAnswers.forEach(answer => {
 // Filter out the products that do not match the answer.
            quizProducts = filterProductsPerAnswer(quizProducts, answer);
        });
 // Extract the product IDs from the filtered product.
        quizProducts = quizProducts.map(quizProduct => quizProduct.productId);
 // Get a random list from the remaining filtered products.
        quizProducts = getRandomItemsFromArray(quizProducts, numberOfSlides);
 // Populate the recommended products repeater with the product data that corresponds to the randomly selected products.
        $w('#recommendedProducts').data = await getProductsData(quizProducts);
    }
}

// Checks if the quiz has been completed.
function isQuizFinished() {
 // Check if the current slide is the last slide.
 return $w('#quizSlides').currentIndex === numberOfSlides - 1;
}

// Get the keywords associated with each product.
async function getKeywordsPerProduct() {
 // Query the "productsSearch" collection for the product keywords.
 let productsSearch = await wixData.query("productsSearch").find();
 // Extract the items from the query results.
    productsSearch = productsSearch.items;
 // Process the returned item data to make it easier to work with.
    productsSearch = productsSearch.map(item => {
 return {
            productId: item.product,
            keywords: item.keywords.split(",")
        }
    });
 // Return the processed item data.
 return productsSearch;
}

// Filter out products that don't match the specified answer. 
function filterProductsPerAnswer(quizProducts, answer) {
 // Use the JavaScript filter() function to filter out products that don't match the specified answer. 
 const filteredProducts = quizProducts.filter(quizProduct => {
 return quizProduct.keywords.includes(answer)
    });
 // Return the filtered product list.
 return filteredProducts;
}

// Get a specified number of random items from the specified array.
function getRandomItemsFromArray(productsArr, numberOfItems){
 // List for storing the randomly selected products.
 const productsIds = [];
 // Number of products in the specified array.
 const numberOfProducts = productsArr.length;

 // For specified the number of items or the number of products that were specified, whichever is lower: 
 for (let i = 0; i < numberOfItems && i < numberOfProducts; i++){
 // Get a random valid index.
 const randomIndex = getRandomInt(0, numberOfProducts -1 );
 // Add the product at that random index to the list of selected products.
        productsIds.push(productsArr[randomIndex]);
 // Remove the already selected product from the list of products to select from.
        productsArr.splice(randomIndex, 1);
    }
 // Return the randomly selected products.
 return productsIds;
}

// Get a random number between two specified values.
function getRandomInt(min, max) {
 // Use JavaScript math functions to get a random number between two specified values.
 return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Get the product data associated with the specified product IDs.
async function getProductsData(productsIds) {
 // Query the "Products" collection for products whose ID is one of the specified IDs.
 const productsData = await wixData.query("products")
        .hasSome("_id", productsIds)
        .find();
 // Return the matching products.
 return productsData.items;
}

// Set up each item in the recommended products repeater when it receives data as the quiz is finished.
export function recommendedProducts_itemReady($item, itemData, index) {
 // Populate the elements from the current item's data.
    $item('#name').text = itemData.name;
    $item('#image').src = itemData.thumbnail;
    $item('#size').text = itemData.size;
    $item('#artist').text = itemData.artist;
}

P.s. There are various bits that are unfinished/not working on the site yet whilst I finish this area first… incase you wondered! :slight_smile:

Try changing the isQuizFinished function to:
function isQuizFinished() {
return $w(‘#quizSlides’).currentIndex === numberOfSlides - 1 ? true : false;
}
Just a precaution for compatibility.

Inside your for loop, your condition implies numberofItems and numberofProducts can be of different length, but you don’t have any condition inside the loop to handle if this is the case, which can lead to errors.

Have you console.logged statements at important breakpoints to check at which specific function the code is failing?

P.S. in the onchange listener, you’re reassigning the quizProducts variable multiple times. I would recommend converting it to a different variable to avoid confusion when debugging.