npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

notify-me-wl

v1.4.0

Published

This JavaScript widget allows users to register their interest in a product that is either "coming soon" or "out of stock". Depending on the `data-type` attribute provided, the widget will display appropriate messages and functionality.

Downloads

17

Readme

Coming Soon/Notify Me Widget

This JavaScript widget allows users to register their interest in a product that is either "coming soon" or "out of stock". Depending on the data-type attribute provided, the widget will display appropriate messages and functionality.

Features:

  • Dynamically display a form to collect user information (mobile, first name, last name).
  • Adjust form and button text based on the widget type (notify-me or coming-soon).
  • Submit user data to a specified API endpoint with email or mobile as a path parameter and appropriate query parameters.

Installation

Add the following script to your HTML to include the widget:

<script src="https://cdn.jsdelivr.net/npm/notify-me-wl/index.js"></script>

Usage

Add a button to your HTML where you want the widget to appear. The button should have an id of popup-open, a data-fields attribute with the fields you want to include in the form (as a JSON string), and a data-type attribute to specify the widget type (notify-me or coming-soon).

<div id="notification-widget"></div>
<button id="popup-open" data-fields='["mobile", "firstName", "lastName"]' data-type="notify-me">Open Popup</button>

Configuration

  • data-fields: A JSON array specifying the fields to include in the form. Possible values are "mobile", "firstName", and "lastName".
  • data-type: Specifies the type of the widget. Possible values are "notify-me" and "coming-soon".

Example

<div id="notification-widget"></div>
<button id="popup-open" data-fields='["mobile"]' data-type="notify-me">Open Popup</button>

<script>
  // The script provided will be included here
</script>

How It Works

  1. Initialization: The script listens for the DOMContentLoaded event to initialize the widget.
  2. Styles Injection: It injects necessary styles for the modal and form elements.
  3. Modal Creation: Creates the modal structure and appends it to the notification-widget div.
  4. Dynamic Content: Based on the data-type, it sets the appropriate messages and button text.
  5. Form Submission: Collects form data and submits it to the API with the mandatory email field and other possible fields.

API Request Example

For sandbox environment

https://api.au-sandbox.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest

For production environment

https://api.au-aws.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest

Full Script

// Access token for the API
const ACCESS_TOKEN = "123"; // Replace `ACCESS_TOKEN` with your actual access token to authenticate API requests.
const TENANT_ID = "victoria-woods"; // Replace `TENANT_ID` with your actual tenant ID.

document.addEventListener("DOMContentLoaded", function () {
   const wrapper = document.getElementById("notification-widget");
   if (!wrapper) return;

   // Define and apply styles for the popup and button
   const styles = `
        .notification-btn {
            background-color: #fff;
            border: 1px solid black;
            color: black;
            padding: 12px 24px;
            text-align: center;
            width: 100%;
            cursor: pointer;
            text-transform: uppercase;
        }
        #popup-body.overlay {
            position: fixed;
            z-index: 9998;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(0, 0, 0, 0.7);
            transition: opacity 200ms;
            visibility: hidden;
            opacity: 0;
        }
        #popup-wrapper {
            margin: 70px auto;
            z-index: 9999;
            padding: 15px;
            background: #fff;
            border-radius: 5px;
            max-width: 360px;
            position: relative;
        }
        #popup-close {
            position: absolute;
            top: 5px;
            right: 15px;
            transition: all 200ms;
            font-size: 30px;
            font-weight: bold;
            text-decoration: none;
            color: #333;
            cursor: pointer;
        }
        #popup-close:hover {
            color: #d80606;
        }
        #popup-title {
            margin: 0;
            font-size: 20px;
            max-width: 350px;
            text-transform: uppercase;
        }
        #popup-text {
            margin: 10px 0;
            font-size: 14px;
        }
        #popup-form {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        #popup-form input {
            padding: 0.475em 1em;
            border: 1px solid #caced1;
            border-radius: 0.25rem;
            font-size: 1.15rem;
            max-width: 100%;
        }
        #popup-form input::placeholder {
            font-size: 1rem;
        }
        #popup-form button {
            font-size: 0.85rem;
            padding: 0.975em 1em;
            font-weight: 600;
            border: none;
            text-transform: uppercase;
            background: #aca475;
            color: #fff;
            cursor: pointer;
        }
        .custom-select {
            position: relative;
        }
        .custom-select select {
            appearance: none;
            width: 100%;
            font-size: 1.15rem;
            padding: 0.475em 1em;
            background-color: #fff;
            border: 1px solid #caced1;
            border-radius: 0.25rem;
            color: #000;
            cursor: pointer;
        }
        .custom-select::before,
        .custom-select::after {
            --size: 0.3rem;
            content: "";
            position: absolute;
            right: 1rem;
            pointer-events: none;
        }
        .custom-select::before {
            border-left: var(--size) solid transparent;
            border-right: var(--size) solid transparent;
            border-bottom: var(--size) solid black;
            top: 40%;
        }
        .custom-select::after {
            border-left: var(--size) solid transparent;
            border-right: var(--size) solid transparent;
            border-top: var(--size) solid black;
            top: 55%;
        }
    `;

   const styleSheet = document.createElement("style");
   styleSheet.type = "text/css";
   styleSheet.innerText = styles;
   document.head.appendChild(styleSheet);

   // Create and append the overlay for the popup
   const overlay = document.createElement("div");
   overlay.innerHTML = `
        <div id="popup-body" class="overlay">
            <div id="popup-wrapper">
                <h3 id="popup-title"></h3>
                <span id="popup-close">&times;</span>
                <div id="popup-text"></div>
                <form id="popup-form">
                    <div class="custom-select">
                        <select name="select-size" required>
                        </select>
                    </div>
                    <input name="email" placeholder="Email" type="email" required />
                </form>
            </div>
        </div>
    `;

   wrapper.appendChild(overlay);

   const popupBody = document.getElementById("popup-body");
   const popupClose = document.getElementById("popup-close");
   const popupOpenButton = document.getElementById("popup-open");
   const popupTitle = document.getElementById("popup-title");
   const popupText = document.getElementById("popup-text");
   const sizeSelect = document.querySelector("select[name='select-size']");
   const form = overlay.querySelector("#popup-form");

   if (!popupBody || !popupClose || !popupOpenButton || !popupTitle || !sizeSelect) return;

   // Parse fields and type to be included in the form from data attribute
   const fields = JSON.parse(popupOpenButton.getAttribute("data-fields") || '["email"]');
   const type = popupOpenButton.getAttribute("data-type") || "notify-me";
   const typeConfig = {
      "notify-me": {
         text: "Register to receive a notification as soon as this item is back in stock",
         buttonText: "Notify me",
      },
      "coming-soon": {
         text: "Register your interest to hear more about this item",
         buttonText: "Register Interest",
      },
   };

   // Get product data from Shopify's global variable
   const productData = window?.currentProduct;

   // Map for form fields
   popupText.innerText = typeConfig[type].text;

   const fieldMap = {
      email: `<input name="email" placeholder="Email" type="email" required />`,
      mobile: `<input name="mobile" placeholder="Mobile" type="tel" required />`,
      firstName: `<input name="firstName" placeholder="First name" type="text" required />`,
      lastName: `<input name="lastName" placeholder="Last name" type="text" required />`,
   };

   // Add fields to the form based on the parsed fields
   fields.forEach((field) => {
      if (fieldMap[field]) {
         form.insertAdjacentHTML("beforeend", fieldMap[field]);
      }
   });

   form.insertAdjacentHTML("beforeend", `<div id="checkbox-wrapper"></div>`);
   const checkboxWrapper = document.getElementById("checkbox-wrapper");
   checkboxWrapper.insertAdjacentHTML("beforeend", `<input type="checkbox" name="mailList" id="mailList" />`);
   checkboxWrapper.insertAdjacentHTML("beforeend", `<label for="mailList">Subscribe to our mailing list</label>`);
   form.insertAdjacentHTML("beforeend", `<button type="submit">${typeConfig[type].buttonText}</button>`);

   // Show/hide the popup
   function showPopup() {
      if (!productData) {
         alert("Product data not found");
         return;
      }
      // Set the popup title and populate the size dropdown
      popupTitle.innerHTML = productData.title;
      productData.variants.forEach((variant) => {
         const option = document.createElement("option");
         option.value = variant.title;
         option.textContent = variant.title;
         sizeSelect.appendChild(option);
      });
      popupBody.style.visibility = "visible";
      popupBody.style.opacity = 1;
   }

   function hidePopup() {
      popupBody.style.visibility = "hidden";
      popupBody.style.opacity = 0;
   }

   popupOpenButton.addEventListener("click", showPopup);
   popupClose.addEventListener("click", hidePopup);

   // Form submission handler
   if (form) {
      form.addEventListener("submit", function (event) {
         event.preventDefault();

         const selectedSize = sizeSelect.value;
         const selectedVariant = productData.variants.find((variant) => variant.title === selectedSize);

         if (selectedVariant) {
            const formData = {
               variantRef: selectedVariant.id,
               email: form.querySelector("input[name='email']").value,
               subscribe: form.querySelector("input[name='mailList']").checked,
            };

            if (type === "coming-soon") {
               formData.comingSoon = true;
               // formData.productRef = productData.id;
            } else {
               formData.notifyMe = true;
            }
            if (fields.includes("firstName")) {
               formData.firstName = form.querySelector("input[name='firstName']").value;
            }
            if (fields.includes("lastName")) {
               formData.lastName = form.querySelector("input[name='lastName']").value;
            }
            if (fields.includes("mobile")) {
               formData.mobile = form.querySelector("input[name='mobile']").value;
               // formData.phone = form.querySelector("input[name='mobile']").value;
            }

            let url = `https://api.au-sandbox.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest`;

            fetch(url, {
               method: "POST",
               headers: {
                  "Content-Type": "application/json",
                  Authorization: ACCESS_TOKEN,
                  "X-Twc-Tenant": TENANT_ID,
               },
               body: JSON.stringify(formData),
            })
               .then((response) => {
                  if (response.ok) {
                     alert("Form submitted");
                     hidePopup();
                  } else {
                     alert("Form submission failed");
                  }
               })
               .catch((error) => {
                  console.error("Error submitting form:", error);
                  alert("Form submission failed");
               });
         } else {
            alert("Selected variant not found");
         }
      });
   }
});

Replace ACCESS_TOKEN with your actual access token to authenticate API requests. Replace TENANT_ID with your actual tenant to authenticate API requests.