Skip to content

Instantly share code, notes, and snippets.

@rashedInt32
Last active March 18, 2025 12:41
Show Gist options
  • Save rashedInt32/c9590d531f314785422d7b6da5ea240d to your computer and use it in GitHub Desktop.
Save rashedInt32/c9590d531f314785422d7b6da5ea240d to your computer and use it in GitHub Desktop.
Inactivity Cart Reminder
/**
* Need to ensure its desktop and chrome browser only
* Crate CONFIG object that holds the constant value
* Create timer function that trigger show modal function
* after 30 seconds of inactivity
*
* add Event listener for mousemove, click and keypress
* * Show modal function should check if the cart empty or not
* * if empty, do nothing. It item present trigger modal with last item
* create show modal function to render html
* Add modal close funcionlity
* create style function for the modal
*
* Create init funciton that check when modal should show or not
* attach event listener function inside init
* lastly call resetTimer inside init
*
** We'll get cart items details from sessionStorage `cart` key {itemCount: number, cartItems: object[]}
*/
/**
* Inactivity Cart Reminder
*/
const InactivityCartReminder = (function () {
function shouldInitialize() {
return window.innerWidth >= 1024 && /Chrome/.test(navigator.userAgent);
}
const CONFIG = {
inactivityTimeout: 30000, // 30 seconds
minScreenWidth: 1024,
storageKey: "cart",
cartPageUrl: "https://www.serenaandlily.com/shoppingbag",
};
let inactivityTimer;
// Reset inactivity timer
function resetTimer() {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(
checkCartAndShowModal,
CONFIG.inactivityTimeout
);
}
// Attach all event listeners
function attachEventListeners() {
["mousemove", "click", "keypress"].forEach((eventType) => {
document.addEventListener(eventType, resetTimer, { passive: true });
});
}
// Check cart and show modal if needed
function checkCartAndShowModal() {
try {
const cart = JSON.parse(sessionStorage.getItem(CONFIG.storageKey)) || {};
const { itemCount, cartItems } = cart;
if (itemCount > 0) {
const lastItem = cartItems[cartItems.length - 1];
showModal(lastItem);
}
} catch (error) {
console.error("Error checking cart:", error);
}
}
// Create and display the modal
function showModal(item) {
// Prevent multiple modals
if (document.querySelector(".icr-modal")) return;
addStyles();
// Create modal elements
const modal = document.createElement("div");
modal.className = "icr-modal";
modal.innerHTML = `
<div class="icr-content">
<div class="icr-header">
<h2 class="icr-title">Buy now before it's too late!</h2>
<button class="icr-close" aria-label="Close">×</button>
</div>
<div class="icr-product">
<img class="icr-product-img" src="${item.imageUrl || ""}" alt="${item.name}">
<div class="icr-details">
<p class="icr-name">${item.name}</p>
${
item.variations.length > 0
? item.variations
.map(
(variant) =>
`<p class="icr-desc"><span class="icr-desc-highlight">${variant.name}: </span>${variant.value}</p>`
)
.join("")
: ""
}
<p class="icr-desc">
Qty: ${item.quantity}
</p>
<p class="icr-price">${item.displayPrice.unitPrice}</p>
</div>
</div>
<div class="icr-footer">
<button class="icr-btn icr-btn-primary view-bag">View Bag</button>
<button class="icr-btn icr-btn-outline continue-shopping">Continue Shopping</button>
</div>
</div>
`;
document.body.appendChild(modal);
document.body.classList.add("icr-modal-open");
// Add event listeners to modal buttons
modal.querySelector(".icr-close").addEventListener("click", closeModal);
modal
.querySelector(".continue-shopping")
.addEventListener("click", closeModal);
modal.querySelector(".view-bag").addEventListener("click", () => {
window.location.href = CONFIG.cartPageUrl;
});
// Close modal when clicking overlay
modal.addEventListener("click", (event) => {
if (event.target === modal) {
closeModal(modal);
}
});
}
// Close the modal
function closeModal() {
const modal = document.querySelector(".icr-modal");
if (modal) {
modal.remove();
document.body.classList.remove("icr-modal-open");
}
}
// Add styles to document
function addStyles() {
if (document.getElementById("icr-styles")) return;
const style = document.createElement("style");
style.id = "icr-styles";
style.textContent = `
.icr-modal {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 99999;
font-family: BeatriceMedium, sans-serif;
}
.icr-content {
background: white;
padding: 16px;
min-width: 400px;
max-width: 500px;
border: 1px solid #a7b0ba;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.icr-header {
position: relative;
display: flex;
padding-bottom: 10px;
border-bottom: 1px solid #a7b0ba;
}
.icr-title {
font-size: 15px;
text-transform: uppercase;
color: #243953;
margin: 0;
font-weight: normal;
}
.icr-close {
position: absolute;
top: -7px;
right: 0px;
width: 35px;
height: 35px;
border: 1px solid #a7b0ba;
border-radius: 30px;
background: none;
font-size: 30px;
cursor: pointer;
}
.icr-product {
display: flex;
align-items: flex-start;
padding-top: 10px;
}
.icr-product-img {
max-width: 100px;
}
.icr-details {
padding-left: 15px;
text-align: left;
}
.icr-name, .icr-price {
font-size: 14px;
margin: 0 0 5px;
}
.icr-desc {
font-size: 13px;
color: #6d7a86;
margin: 0;
}
.icr-desc-highlight {
color: #243953;
font-weight: bold;
}
.icr-footer {
margin-top: 20px;
padding-top: 15px;
display: flex;
justify-content: center;
gap: 15px;
border-top: 1px solid #a7b0ba;
}
.icr-btn {
border-radius: 9999px;
font-size: 15px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 16px 40px 13px;
transition: all .3s ease;
cursor: pointer;
}
.icr-btn-outline {
border: 0;
background: transparent;
color: #243953;
padding: 16px 0;
}
.icr-btn-primary {
background-color: #243953;
border: 1px solid #243953;
color: white;
font-weight: 600;
}
.icr-btn-outline:hover {
color: rgba(36, 57, 83, 0.6);
}
.icr-btn-primary:hover {
background-color: #acbecf;
border-color: #acbecf;
color: #243953;
}
body.icr-modal-open {
overflow: hidden !important;
}
`;
document.head.appendChild(style);
}
// Initialize the module
function init() {
if (!shouldInitialize()) {
return;
}
attachEventListeners();
resetTimer();
}
return {
init,
};
})();
// Initialize the module when the DOM is fully loaded
InactivityCartReminder.init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment