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

@dotdev/reactive-rewards

v2.3.6

Published

AP21 rewards react library

Downloads

6

Readme

Reactive Apparel21 Rewards

The front end react library for the AP21 rewards integration.

Install

To install simply run npm install @dotdev/reactive-rewards

Setup Shopify store

There are a few requirements for this integration. This integration requires Shopify scripts therefore it is only available to Shopify Plus partners.

  1. Install the Shopify Scripts and create a new line item script with [this script][c67a5b66]. The Shopify script will apply any rewards to cart and reduce benefits price down to 0.
  2. Create a new private app with storefront access to unauthenticated_read_product_listings, unauthenticated_read_product_tags, unauthenticated_read_product_inventory. This will be used to fetch reward and gift products asynchronously. [c67a5b66]: #shopify-script "Shopify Script"
  3. Create a reward product. This product will need to have a price of 0.

Usage

Once installed the Loyalty components can be imported like this.

import * as Loyalty from "@dotdev/reactive-rewards"

Provider

All loyalty components should be wrapped within the Provider component. The provider component takes the Shopify customer information and uses it to request the Apparel21 customer. The Apparel21 customer is then passed into the child components. It also takes other store specific configurations.

<Loyalty.Provider
  customer={account.customer}
  rewardId={32843578376280}
  url={'https://connector-staging.cable-integration.io'}
  storefront={{
    key: '41e72449d9ae377baa2363774f504fd2',
    url: window.theme.permanentDomain,
  }}
  theme={{
    typography: {
      headings: {
        h3: {
          fontFamily: 'Chronicle, Helvetica Neue, Arial, sans-serif',
          fontSize: '2rem'
        },
        h4: {
          fontSize: '1.25rem'
        }
      }
    },
    colors: {
      primary: '#000',
      secondary: '#fff',
    },
    spacing: {
      vertical: 5,
      horizontal: 5
    },
    container: {
      width: 1440,
    },
    image: {
      borderStyle: 'solid',
      borderWidth: 1,
    },
    button: {
      borderStyle: 'solid',
      borderWidth: 1,
      textTransform: 'uppercase',
      letterSpacing: '0.075rem',
      fontSize: '1rem',
      fontWeight: 600,
    },
  }}
>
  <Loyalty.Benefits
    title={"Gifts & Rewards"}
    redeemable
  />
</Loyalty.Provider>

prop | description | required -------------|------------------------------------------|------------------- customer | An object representing Shopify customer information containing first_name, last_name, token, id. Read below about how to generate a token. | yes rewardId | A variant id representing a reward placeholder product in Shopify. | yes url | An optional url prefix for using a staging integration or changing the proxy url | no storefront | An object that takes the storefront api key and the store url. | yes theme | An object representing the styles that you would like to pass to the components. Any of the keys you provide will overwrite the default styling but you do not need to provide all of them. | no

Generating a Token

For security reasons Liquid should be used to generate a token so that the secret is not exposed to the front end. Each customer will have a unique id and each store will have a different secret by combining the 2 with hmac_sha256 we can create a secret token for fetching the Apparel21 user.

{{ customer.metafields.ap21.person_id | hmac_sha256: settings.loyalty_secret | json }}

You may want to store the secret in the theme settings, especially if the store has multiple regions.

{
  "name": "Loyalty",
  "settings": [
    {
      "type": "text",
      "id": "loyalty_secret",
      "label": "Loyalty secret"
    }
  ]
},

Basic Components

There are a two base components (Account and Benefits) which are designed to work "out of the box" and reduce the amount of time it takes to get started. These components can be customised simply by using the theme prop on the Provider component. If you need to customise the layout of the component you can use the render prop.

Account

Displays basic account information such as current tier, the spend to the next tier, the date they achieved the current tier. It also has an optional render prop so that you can fully customise the structure and layout of this component.

Benefits

Displays the current rewards and benefits to the customer. When the redeemable prop is used it will change the layout to display buttons which will allow the gifts to be redeemed. The redeemable prop can technically be used anywhere on the site but would typically be used on the cart page.

Advanced components

If you want to have complete control over the rendering of the components there are two options. Either use the Consumer component or hooks. If you would like to use hooks read below.

Consumer

Example

<Loyalty.Provider
  customer={customer}
  rewardId={32843578376280}
  url={'https://connector-staging.cable-integration.io'}
  storefront={{
    key: '41e72449d9ae377baa2363774f504fd2',
    url: window.theme.permanentDomain,
  }}
>
  <Loyalty.Consumer>
    {({ account }) => {
      if (!account) {
        return <React.Fragment />
      }
      const name = account.tier_name as 'Cotton' | 'Merino' | 'Cashmere'
      const tier = tiers[name]
      return (
        <div className="w-1/3 p-1">
          <div className="bg-grey-100">
            <div className="p-4">
              <p className="font-heading text-2xl tracking-widest mt-1">
                <span className="uppercase">{name}</span> tier
              </p>
              <ul className="mb-2 list-disc uppercase text-xs">
                {tier.features.split("\n").map((item) => (
                  <li key={item}>
                    {item}
                  </li>
                ))}
              </ul>
              <h3 className="font-heading italic">
                Annual Spend<br/>
                {tier.spend}
              </h3>
            </div>
          </div>
        </div>
      )
    }}
  </Loyalty.Consumer>
</Loyalty.Provider>

The consumer is provided with the context as a render prop.

Hooks

useAccount

Get the Apparel21 account

useBenefits

Get the Available rewards

useCart

Get the Shopify cart

Shopify Script


### Begin loyalty ###

# Check if cart contains ordinary product
cartContainsOrdinaryProduct = false
Input.cart.line_items.each do |line_item|
  if (
    !line_item.properties["_gift_id"]
  ) then
    cartContainsOrdinaryProduct = true
    break
  end
end

# Calculate subtotal for reward eligibility (inc discount codes)
rewardSubtotal = Input.cart.subtotal_price
if Input.cart.discount_code && !Input.cart.discount_code.rejected?
  if Input.cart.discount_code.is_a?(CartDiscount::Percentage)
    rewardSubtotal -= (rewardSubtotal * (Input.cart.discount_code.percentage/ 100))
  elsif Input.cart.discount_code.is_a?(CartDiscount::FixedAmount)
    rewardSubtotal -= Input.cart.discount_code.amount
  end
end

# Gifts only allowed for carts with an ordinary product
appliedGiftIds = []
Input.cart.line_items.each do |line_item|
  next unless line_item.properties["_gift_id"]

  if !cartContainsOrdinaryProduct || appliedGiftIds.include?(line_item.properties["_gift_id"])
    line_item.change_properties({ "remove" => true }, message: "Remove")
  end

  appliedGiftIds.push(line_item.properties["_gift_id"])
end
Input.cart.line_items.delete_if { |item| item.properties["remove"] }

rewardAmount = Money.zero()

Input.cart.line_items.each do |line_item|
  product = line_item.variant.product
  if product.product_type == "Loyalty Reward" then
    rewardAmount += Money.new(cents: line_item.properties["reward_amount"].to_i)
  end
  if line_item.properties["_gift_id"]
    line_item.change_line_price(line_item.line_price - line_item.variant.price, message: "Gift Redemption")
  end
end

Input.cart.line_items.each do |line_item|
  product = line_item.variant.product
  if (product.product_type != "Loyalty Reward" && product.product_type != "Gift Card") then
    if (line_item.line_price.cents < rewardAmount.cents)
      rewardAmount -= line_item.line_price
      line_item.change_line_price(Money.zero(), message: "Reward Redemption")
    else
      line_item.change_line_price(line_item.line_price - rewardAmount, message: "Reward Redemption")
      rewardAmount = Money.zero()
    end
  end
end

### End loyalty ###
Output.cart = Input.cart