Skip to main content
Tapcart - scripts.js documentation for the JS tab

Copy the following piece of code into the JS section of your Rise.ai custom block

Itay Abraham avatar
Written by Itay Abraham
Updated over 3 months ago
// ENTER YOUR RISE.AI TAPCART API KEY BELOW
const riseApiKey = 'ENTER YOUR RISE.AI TAPCART API KEY HERE';
// ENTER YOUR MYSHOPIFY.COM URL BELOW
const myShopifyUrl = 'ENTER YOUR MYSHOPIFY.COM URL HERE';
// Edit the values below to change the text / language
const blockText = {
formTitle: "Let's send your gift card!",
formDescription: "We'll directly email the recipient a link to open your greeting card and redeem your gift.",
formTriggerButton: "Send as a Gift",
nameLabel: "Name:",
namePlaceholder: "Recipient's Name",
emailLabel: "Email:",
emailPlaceholder: "Recipient's Email",
greetingLabel: "Greeting:",
greetingPlaceholder: "Enjoy your gift!",
now: "Now",
chooseDate: "Choose Date",
addToCartLabel: "Send Gift",
noName: "Recipient name is required.",
nameLength: "Input is invalid, please keep to less than 40 characters.",
nameEmoji: "Input is invalid. Only text allowed.",
noEmail: "This is not a valid email",
emailFormat: "This is not a valid email",
greetingLength: "Input is invalid, please keep to less than 280 characters.",
greetingEmoji: "Input is invalid. Only text allowed.",
noDate: "Date is required.",
pastDate: "Only today and future dates are allowed.",
formImage: 'https://rise-static-assets.s3.amazonaws.com/integrations/tap-cart/image.png'
}

// DO NOT EDIT BELOW
let selectedVariant = Tapcart.variables.product.selectedVariant.id;
const riseApiBaseUrl = 'https://application.rise-ai.com/integrations/inbound/gifting/session/';
const closeModal = document.getElementById('close_modal');
const riseContainer = document.getElementById('rise_container');
const riseGiftButton = document.getElementById('rise_gift_button');
const riseATC = document.getElementById('rise_atc');
const form = document.getElementById('rise_gift_form');
const dateNow = document.getElementById('date_now');
const datePicker = document.getElementById('date_picker');
const datepickerText = document.getElementById('datepicker_text');
const today = new Date().toISOString().split("T")[0];
const dateInput = document.getElementById('rise_date');
// Set earliest available date to today
dateInput.min = today;
// Set default date to today
dateInput.valueAsDate = new Date();

// Edit the default text if changed above
const setText = () => {
riseGiftButton.innerText = blockText.formTriggerButton;
document.querySelector('.form_title').innerText = blockText.formTitle;
document.querySelector('.form_description').innerText = blockText.formDescription;
document.querySelector('.name_label').innerText = blockText.nameLabel;
document.querySelector('.email_label').innerText = blockText.emailLabel;
document.querySelector('.greeting_label').innerText = blockText.greetingLabel;
document.querySelector('.now_button').innerText = blockText.now;
document.querySelector('#datepicker_text').innerText = blockText.chooseDate;
riseATC.innerText = blockText.addToCartLabel;
document.querySelector('#rise_name').setAttribute('placeholder', blockText.namePlaceholder);
document.querySelector('#rise_email').setAttribute('placeholder', blockText.emailPlaceholder);
document.querySelector('#rise_greeting').setAttribute('placeholder', blockText.greetingPlaceholder);
document.querySelector('#rise_form_img').setAttribute('src', blockText.formImage);
}
setText();

// Function to create gifting session
const createGiftingSession = async() => {
if (!window.riseSessionId) {
try {
const response = await fetch(riseApiBaseUrl, {
method: 'POST',
headers: {
'x-rise-gifting-api-key': riseApiKey
}
});

const json = await response.json();
window.riseSessionId = json.session.id;

} catch (err) {
console.log('Error in creating gifting session: ', err);
}
}
};

// Create the gifting session on page load
createGiftingSession();

// Register event handler for when selectedVariant is changed
Tapcart.registerEventHandler('product/updated', function(eventData) {
selectedVariant = Tapcart.variables.product.selectedVariant.id;
});

// Set BrandKit styles
document.body.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.coreColors.pageColor || '#FFFFFF';
form.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.coreColors.pageColor || '#FFFFFF';
form.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';
riseGiftButton.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryFill || '#d52579';
riseGiftButton.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText || '#FFFFFF';
riseATC.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryFill || '#d52579';
riseATC.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText || '#FFFFFF';
dateNow.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryFill || '#d52579';
dateNow.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText || '#000000';
datePicker.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';
datepickerText.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';

// Send as a gift button event listener
riseGiftButton.addEventListener('click', () => {
// open the form
form.classList.add('open');
form.classList.remove('hidden');
riseGiftButton.classList.add('hidden');
// pause for animation, then scroll to block bottom
setTimeout(() => {
Tapcart.actions.scrollToBlockBottom();
}, 550)
});

// Function to close the recipient form modal
closeModal.addEventListener('click', () => {
form.classList.remove('open');
form.classList.add('hidden');
riseGiftButton.classList.remove('hidden');
})

// Function to validate name field
const validateName = (name) => {
if (!name) {
// We don't have a name
return {
'validity': false,
'message': blockText.noName
}
} else if (name.length > 40) {
// Length of name is too long
return {
'validity': false,
'message': blockText.nameLength
}
} else if (/\p{Extended_Pictographic}/u.test(name)) {
// Emoji included in name
return {
'validity': false,
'message': blockText.nameEmoji
}
} else {
// Must be valid at this point
return true;
}
}

// Function to validate email field
const validateEmail = (email) => {
const match = email.match(
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
if (!email) {
// We don't have an email
return {
'validity': false,
'message': blockText.noEmail
}
} else if (!match) {
// Email text is not a valid email format
return {
'validity': false,
'message': blockText.emailFormat
}
} else {
// Must be valid at this point
return true;
}
}

// Function to validate greeting field
const validateGreeting = (greeting) => {
if (greeting.length > 280) {
// Length of greeting is too long
return {
'validity': false,
'message': blockText.greetingLength
}
} else if (/\p{Extended_Pictographic}/u.test(greeting)) {
// Emoji included in greeting
return {
'validity': false,
'message': blockText.greetingEmoji
}
} else {
// Must be valid at this point
return true;
}
}

// Function to validate date field
const validateDate = (date) => {
const errorMsg = document.getElementById('date_err');
if (!date) {
// We don't have a date
return {
'validity': false,
'message': blockText.noDate
}
} else if (date < today) {
// Date is before today
return {
'validity': false,
'message': blockText.pastDate
}
} else {
// Must be valid at this point
return true;
}
}

// Function to validate all form fields for ATC button state
const validateForm = () => {
const name = document.getElementById('rise_name').value;
const email = document.getElementById('rise_email').value;
const greeting = document.getElementById('rise_greeting').value || '';

return (validateName(name) === true && validateEmail(email) === true && validateGreeting(greeting) === true && validateDate(dateInput.value) === true)
}

const setError = (error, form, message, type) => {
error.innerText = message;
error.classList.remove('hidden');
form.classList.add(type);
}

const removeError = (error, form, type) => {
error.classList.add('hidden');
form.classList.remove(type);
}

// Name input event listener
document.getElementById('rise_name').addEventListener('input', (e) => {
const errorMsg = document.getElementById('name_err');
const result = validateName(e.target.value);
if (result.validity === false) {
setError(errorMsg, form, result.message, 'name_validation_err');
} else {
removeError(errorMsg, form, 'name_validation_err');
if (validateForm()) {
riseATC.removeAttribute('disabled');
}
}
});

// Email input event listener
document.getElementById('rise_email').addEventListener('input', (e) => {
const errorMsg = document.getElementById('email_err');
const result = validateEmail(e.target.value);
if (result.validity === false) {
setError(errorMsg, form, result.message, 'email_validation_err');
} else {
removeError(errorMsg, form, 'email_validation_err');
if (validateForm()) {
riseATC.removeAttribute('disabled');
}
}
});

// Greeting input event listener
document.getElementById('rise_greeting').addEventListener('input', (e) => {
const errorMsg = document.getElementById('greeting_err');
const result = validateGreeting(e.target.value);
if (result.validity === false) {
setError(errorMsg, form, result.message, 'greeting_validation_err');
} else {
removeError(errorMsg, form, 'greeting_validation_err');
if (validateForm()) {
riseATC.removeAttribute('disabled');
}
}
});

// Date input event listener
dateInput.addEventListener('input', (e) => {
const errorMsg = document.getElementById('date_err');
const result = validateDate(e.target.value);
if (result.validity === false) {
setError(errorMsg, form, result.message, 'date_validation_err');
} else {
removeError(errorMsg, form, 'date_validation_err');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const datetime = new Date(e.target.value);
dateNow.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.coreColors.pageColor || '#FFFFFF';
dateNow.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';
datepickerText.innerText = `${months[datetime.getMonth()]} ${datetime.getDate() + 1}, ${datetime.getFullYear()}`;
datepickerText.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText;
datePicker.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryFill;
datePicker.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText;
if (validateForm()) {
riseATC.removeAttribute('disabled');
}
}
})

// Date now button event listener
dateNow.addEventListener('click', (e) => {
dateInput.valueAsDate = new Date();
dateNow.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryFill;
dateNow.style.color = Tapcart.variables.theme?.tokens.colors.buttonColors.secondaryText;
datePicker.style.backgroundColor = Tapcart.variables.theme?.tokens.colors.coreColors.pageColor || '#FFFFFF';
datePicker.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';
datepickerText.style.color = Tapcart.variables.theme?.tokens.colors.textColors.primaryColor || '#000000';
datepickerText.innerText = 'Choose Date';
})

function tapcartAddToCart(name, email, greeting, value, gift_id) {
console.log('adding to cart');
Tapcart.actions.addToCart({
lineItems: [
{
variantId: selectedVariant,
quantity: 1,
attributes: [
{
key: 'value',
value: value
},
{
key: 'name',
value: name
},
{
key: 'email',
value: email
},
{
key: 'message',
value: greeting
},
{
key: '_sendAt',
value: dateInput.value
},
{
key: 'image',
value: Tapcart.variables.product.images[0]
},
{
key: '_sendingMethod',
value: 'email'
},
{
key: '_gift_id',
value: gift_id
},
{
key: '_rise_session_id',
value: window.riseSessionId
}
]
}
]
})
}

async function createRiseGift(giftData, giftUrl) {
const response = await fetch(giftUrl, {
method: 'POST',
headers: {
'x-rise-gifting-api-key': riseApiKey,
'Content-Type': 'application/json'
},
body: giftData
});

const json = await response.json();

return json.gift.gift_id;
}

// Add to cart event listener
riseATC.addEventListener('click', async (e) => {
const name = document.getElementById('rise_name').value;
const email = document.getElementById('rise_email').value;
const greeting = document.getElementById('rise_greeting').value || blockText.greetingPlaceholder;
let value;

for (const variant of Tapcart.variables.product.variants) {
if (variant.id === selectedVariant) {
value = variant.price;
}
}

const giftData = JSON.stringify({
gift: {
value: value,
variant_id: selectedVariant,
name: name,
email: email,
message: greeting,
sending_method: 'email',
image: Tapcart.variables.product.images[0],
send_at: dateInput.value
}
})


const giftUrl = `${riseApiBaseUrl + window.riseSessionId}?shop_url=${myShopifyUrl}`;
try {
const gift_id = await createRiseGift(giftData, giftUrl);
tapcartAddToCart(name, email, greeting, value, gift_id);
} catch (err) {
console.log('error: ', err);
}
})
Did this answer your question?