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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@akad/loyalty-modals

v0.0.7

Published

Drop-in React modals for the Akad loyalty program. It fetches the broker's loyalty status and renders the right modal at the right time:

Downloads

326

Readme

@akad/loyalty-modals

Drop-in React modals for the Akad loyalty program. It fetches the broker's loyalty status and renders the right modal at the right time:

  • Invite — prompts eligible brokers to join the program.
  • Level unlocked — celebrates a newly unlocked level and links to the club.

The host app owns data fetching transport (via an injected client) and the QueryClientProvider; this package owns the decision logic and UI.

Install

Install the package from npm:

"@akad/loyalty-modals": "^0.0.5"

See loyalty-modals-release.md for the release flow.

Local playground

Run the package-local modal playground with:

yarn workspace @akad/loyalty-modals dev:playground

The playground lives under packages/loyalty-modals/playground and injects a fake LoyaltyApiClient directly into LoyaltyModalsProvider. It is not exported from src/index.ts, is not included in the published package files, and does not use the dashboard SDK, routes, mock headers, or fixture switcher.

Peer dependencies

react, react-dom, @tanstack/react-query, @akad/sdk, and @akad/design-system must be provided by the host.

Usage

import {
  createLoyaltyClient,
  LoyaltyModals,
  LoyaltyModalsProvider,
} from '@akad/loyalty-modals';
import '@akad/loyalty-modals/style.css';

const client = createLoyaltyClient(sdkBase); // any { request } requester

function App() {
  return (
    <LoyaltyModalsProvider
      client={client}
      clubUrl="https://club.akad.com.br"
      onAccessClub={() => navigate('/club')} // optional, see below
      tracking={{
        onEvent: (event) => trackLoyaltyModal(event),
      }}
    >
      <LoyaltyModals onError={(error, traceId) => report(error, traceId)} />
    </LoyaltyModalsProvider>
  );
}

Mount LoyaltyModalsProvider inside your existing QueryClientProvider.

The host SDK instance must already be configured for the Loyalty Program API:

sdkInitialize({
  baseUrl: '<APIM gateway base URL>',
  productCode: 'loyalty-program',
  apiVersion: 'v1',
});

createLoyaltyClient sends relative endpoints (/status, /accept, and /mark-unlocked-viewed) through that requester. Do not configure the host SDK base URL with a Loyalty path suffix; the SDK adds product and version routing.

API

LoyaltyModalsProvider

| Prop | Type | Required | Description | | -------------- | ----------------------- | -------- | ----------------------------------------------------------- | | client | LoyaltyApiClient | yes | Reads status and records accept / view actions. | | clubUrl | string | yes | Destination used for the default hard redirect to the club. | | onAccessClub | () => void | no | Navigation override (see below). | | tracking | LoyaltyModalsTracking | no | Optional modal interaction callback API owned by the host. |

LoyaltyModals

| Prop | Type | Default | Description | | --------- | -------------------------------------------- | ------- | -------------------------------------------------------- | | enabled | boolean | true | When false, skips the status request and renders null. | | onError | (error: unknown, traceId?: string) => void | — | Called for status and mutation failures. |

createLoyaltyClient(base)

Builds a LoyaltyApiClient from a requester exposing request(endpoint, { method, data }) (e.g. the @akad/sdk base), targeting the loyalty-program endpoints.

Requests created by this adapter include tracking: {} so hosts using @akad/sdk + @akad/data-owl emit request events through the SDK tracking pipeline. Mutation requests also include body context in the tracking config: accepted for /accept; action and levelId for /mark-unlocked-viewed. No analytics package is imported by @akad/loyalty-modals.

Navigation

Both the invite accept button and the level-unlocked access club button navigate to the club on success:

  • If onAccessClub is provided, it is called (use this for SPA routing).
  • Otherwise the package does a hard redirect via window.location.assign(clubUrl).

Navigation only runs after the underlying call succeeds; failures are surfaced through onError and (for invite accept) an inline retry message.

Tracking

The package does not import analytics libraries. To track modal interactions, pass tracking.onEvent and map the semantic event to your host app analytics contract:

<LoyaltyModalsProvider
  client={client}
  clubUrl="https://club.akad.com.br"
  tracking={{
    onEvent: (event) => {
      const eventLabels = {
        inviteShown: 'view-clube-convite',
        inviteAccepted: 'click-clube-convite-aceite',
        inviteDeferred: 'click-clube-convite-recusa',
        inviteAcceptError: 'click-clube-convite-erro',
        levelUnlockedShown: 'view-clube-nivel-desbloqueado',
        levelUnlockedAccessedClub: 'click-clube-nivel-acessar-clube',
        levelUnlockedClosed: 'click-clube-nivel-fechar',
      } as const;

      pushClickEvent({
        businessFlow: 'fidelidade',
        contextOrigin: 'clube',
        eventLabel: eventLabels[event.type],
      });
    },
  }}
>
  <LoyaltyModals />
</LoyaltyModalsProvider>

tracking.onEvent receives this union:

type LoyaltyModalEvent =
  | { type: 'inviteShown' }
  | { type: 'inviteAccepted' }
  | { type: 'inviteDeferred' }
  | { type: 'inviteAcceptError' }
  | { levelId: string; levelName: string; type: 'levelUnlockedShown' }
  | { levelId: string; levelName: string; type: 'levelUnlockedAccessedClub' }
  | { levelId: string; levelName: string; type: 'levelUnlockedClosed' };

inviteShown and levelUnlockedShown fire once per rendered modal key. Action events fire when the user invokes the corresponding modal action, except inviteAccepted, which fires only after the accept request succeeds.