@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
Keywords
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:playgroundThe 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
onAccessClubis 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.
