{"_id":"@akad/loyalty-modals","_rev":"5-4bf6f00088125028a4266bbf9e6efbf9","name":"@akad/loyalty-modals","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.5":{"name":"@akad/loyalty-modals","version":"0.0.5","_id":"@akad/loyalty-modals@0.0.5","maintainers":[{"name":"gabemule","email":"gabemule@gmail.com"},{"name":"carlos.moura","email":"carlos.moura@akadseguros.com.br"},{"name":"gabriel_rizzo","email":"gabriel96.gsr@gmail.com"},{"name":"7i4g0","email":"gtiago.brito@hotmail.com"}],"dist":{"shasum":"cec86a3121d2431c20c38e67cfb33f8e523569e8","tarball":"https://registry.npmjs.org/@akad/loyalty-modals/-/loyalty-modals-0.0.5.tgz","fileCount":6,"integrity":"sha512-ChHWPPoq/RpDjKX9D+i1VcmKqylekoY9cVpGrpKuVBcJBzUjP9VyUQSaKMiJQBAIrYeXU+c3Gel1N1+cwpLYJg==","signatures":[{"sig":"MEQCICu+i64fnYZh6UCRinpGfhpitkzujLiKitHhKLn1BXLgAiBYkGbw92pc2vkAdhVzVPhnHW3YY8ca3GsyRbBp7RoI7w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61263},"type":"module","types":"./index.d.ts","module":"./index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./style.css":"./style.css"},"gitHead":"f9c7c9e153cbb3c95bb2d565b2beee8f29f2780c","_npmUser":{"name":"gabemule","email":"gabemule@gmail.com"},"_npmVersion":"10.9.8","description":"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:","directories":{},"_nodeVersion":"22.23.0","_hasShrinkwrap":false,"peerDependencies":{"react":"^18.2.0","@akad/sdk":"^1.0.30","react-dom":"^18.2.0","@akad/design-system":"^1.1.10","@tanstack/react-query":"^5.90.12"},"_npmOperationalInternal":{"tmp":"tmp/loyalty-modals_0.0.5_1782934553525_0.3927076239138314","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@akad/loyalty-modals","version":"0.0.6","_id":"@akad/loyalty-modals@0.0.6","maintainers":[{"name":"gabemule","email":"gabemule@gmail.com"},{"name":"carlos.moura","email":"carlos.moura@akadseguros.com.br"},{"name":"gabriel_rizzo","email":"gabriel96.gsr@gmail.com"},{"name":"7i4g0","email":"gtiago.brito@hotmail.com"}],"dist":{"shasum":"0c09a7b0d760c0df5b5a1c6e22e3ff68336a3603","tarball":"https://registry.npmjs.org/@akad/loyalty-modals/-/loyalty-modals-0.0.6.tgz","fileCount":6,"integrity":"sha512-IEP8+K83h005V8uKNOAH2T0aA/hf+Ew9LB1ykL6YW1o0JrrYc+pjUznyev3Z0dG3ipFqNe74nlcR3hA6edgMZw==","signatures":[{"sig":"MEUCIQCcPblyUL36v0kke/GHwZw+j1q0XcANQGOm5VYFI2p7/gIgPSZLZDEC6s1RpqlSYj0o1U6vxAguQtyyvw9muwjWLYc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61263},"type":"module","types":"./index.d.ts","module":"./index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./style.css":"./style.css"},"gitHead":"f9c7c9e153cbb3c95bb2d565b2beee8f29f2780c","_npmUser":{"name":"gabemule","email":"gabemule@gmail.com"},"_npmVersion":"10.9.8","description":"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:","directories":{},"_nodeVersion":"22.23.0","_hasShrinkwrap":false,"peerDependencies":{"react":"^18.2.0","@akad/sdk":"^1.0.30","react-dom":"^18.2.0","@akad/design-system":"^1.1.10","@tanstack/react-query":"^5.90.12"},"_npmOperationalInternal":{"tmp":"tmp/loyalty-modals_0.0.6_1782934874032_0.8072038357847953","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"@akad/loyalty-modals","version":"0.0.7","_id":"@akad/loyalty-modals@0.0.7","maintainers":[{"name":"gabemule","email":"gabemule@gmail.com"},{"name":"carlos.moura","email":"carlos.moura@akadseguros.com.br"},{"name":"gabriel_rizzo","email":"gabriel96.gsr@gmail.com"},{"name":"7i4g0","email":"gtiago.brito@hotmail.com"}],"dist":{"shasum":"b2bde0cec3efe6846b45b0dc275ed277f2d9b662","tarball":"https://registry.npmjs.org/@akad/loyalty-modals/-/loyalty-modals-0.0.7.tgz","fileCount":6,"integrity":"sha512-QROeJKIL3K6Xz2SbPGKH3HgyiV578h2N1479vc9oC9UfTopi3D9sU0lYpY07Dow7pDQQ1nkwBWuuqHdafrarVQ==","signatures":[{"sig":"MEQCIB9wnHVnnAoSQxGICxUnoA57fmlaCzofIaeh+yAjWl3bAiA3M9uNz64zAmBDisEyEapXBMrbf2jxZnUwVavK61UlPA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61263},"type":"module","types":"./index.d.ts","module":"./index.js","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./style.css":"./style.css"},"gitHead":"f9c7c9e153cbb3c95bb2d565b2beee8f29f2780c","_npmUser":{"name":"gabemule","email":"gabemule@gmail.com"},"_npmVersion":"10.9.8","description":"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:","directories":{},"_nodeVersion":"22.23.0","_hasShrinkwrap":false,"peerDependencies":{"react":"^18.2.0","@akad/sdk":"^1.0.30","react-dom":"^18.2.0","@akad/design-system":"^1.1.10","@tanstack/react-query":"^5.90.12"},"_npmOperationalInternal":{"tmp":"tmp/loyalty-modals_0.0.7_1782935253538_0.6095849173882208","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@akad/loyalty-modals","version":"0.2.0","type":"module","module":"./index.js","types":"./index.d.ts","exports":{".":{"types":"./index.d.ts","import":"./index.js"},"./style.css":"./style.css"},"peerDependencies":{"@akad/design-system":"^2.0.0","@akad/sdk":"^2.0.0","@tanstack/react-query":"^5.90.12","react":"^18.2.0","react-dom":"^18.2.0"},"_id":"@akad/loyalty-modals@0.2.0","gitHead":"662a6993c97fd76838a96c5673d5bc99aa3e8df3","description":"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:","_nodeVersion":"22.23.1","_npmVersion":"10.9.8","dist":{"integrity":"sha512-GXWloP/mDHo8+Q21ZPnHCCi8SFQWwLsCLnzrQdby3ZEDYJosiyyvTWiPr8v92jYKIcPq9j1k4KFD2CXpB3Fjjg==","shasum":"d1d20886d82367582598cb642e798bfbe0727fa2","tarball":"https://registry.npmjs.org/@akad/loyalty-modals/-/loyalty-modals-0.2.0.tgz","fileCount":6,"unpackedSize":65271,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC5Q+bTgfb7oJDBpZJcwcVjwTG/QVgnwWDEFbyvi03gRAIgceT3vS0sb5wT0/NjL0YmCc+fwW38Y9YWX6OnSmWb82c="}]},"_npmUser":{"name":"gabemule","email":"gabemule@gmail.com"},"directories":{},"maintainers":[{"name":"gabemule","email":"gabemule@gmail.com"},{"name":"carlos.moura","email":"carlos.moura@akadseguros.com.br"},{"name":"gabriel_rizzo","email":"gabriel96.gsr@gmail.com"},{"name":"7i4g0","email":"gtiago.brito@hotmail.com"},{"name":"contratostech","email":"itcontracts@akadseguros.com.br"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/loyalty-modals_0.2.0_1785793659150_0.08492918225725621"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-01T19:35:53.226Z","modified":"2026-08-03T21:47:39.568Z","0.0.5":"2026-07-01T19:35:53.768Z","0.0.6":"2026-07-01T19:41:14.163Z","0.0.7":"2026-07-01T19:47:33.692Z","0.2.0":"2026-08-03T21:47:39.290Z"},"description":"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:","maintainers":[{"name":"gabemule","email":"gabemule@gmail.com"},{"name":"carlos.moura","email":"carlos.moura@akadseguros.com.br"},{"name":"gabriel_rizzo","email":"gabriel96.gsr@gmail.com"},{"name":"7i4g0","email":"gtiago.brito@hotmail.com"},{"name":"contratostech","email":"itcontracts@akadseguros.com.br"}],"readme":"# @akad/loyalty-modals\n\nDrop-in React modals for the Akad loyalty program. It fetches the broker's\nloyalty status and renders the right modal at the right time:\n\n- **Invite** — prompts eligible brokers to join the program.\n- **Level unlocked** — celebrates a newly unlocked level and links to the club.\n\nThe host app owns data fetching transport (via an injected client) and the\n`QueryClientProvider`; this package owns the decision logic and UI.\n\n## Install\n\nInstall the package from npm:\n\n```jsonc\n\"@akad/loyalty-modals\": \"^0.0.5\"\n```\n\nSee [`loyalty-modals-release.md`](./loyalty-modals-release.md) for the release flow.\n\n## Local playground\n\nRun the package-local modal playground with:\n\n```sh\nyarn workspace @akad/loyalty-modals dev:playground\n```\n\nThe playground lives under `packages/loyalty-modals/playground` and injects a\nfake `LoyaltyApiClient` directly into `LoyaltyModalsProvider`. It is not exported\nfrom `src/index.ts`, is not included in the published package files, and does\nnot use the dashboard SDK, routes, mock headers, or fixture switcher.\n\n### Peer dependencies\n\n`react`, `react-dom`, `@tanstack/react-query`, `@akad/sdk`, and\n`@akad/design-system` must be provided by the host.\n\n## Usage\n\n```tsx\nimport {\n  createLoyaltyClient,\n  LoyaltyModals,\n  LoyaltyModalsProvider,\n} from '@akad/loyalty-modals';\nimport '@akad/loyalty-modals/style.css';\n\nconst client = createLoyaltyClient(sdkBase); // any { request } requester\n\nfunction App() {\n  return (\n    <LoyaltyModalsProvider\n      client={client}\n      clubUrl=\"https://club.akad.com.br\"\n      onAccessClub={() => navigate('/club')} // optional, see below\n      tracking={{\n        onEvent: (event) => trackLoyaltyModal(event),\n      }}\n    >\n      <LoyaltyModals onError={(error, traceId) => report(error, traceId)} />\n    </LoyaltyModalsProvider>\n  );\n}\n```\n\nMount `LoyaltyModalsProvider` inside your existing `QueryClientProvider`.\n\nThe host SDK instance must already be configured for the Loyalty Program API:\n\n```ts\nsdkInitialize({\n  baseUrl: '<APIM gateway base URL>',\n  productCode: 'loyalty-program',\n  apiVersion: 'v1',\n});\n```\n\n`createLoyaltyClient` sends relative endpoints (`/status`, `/accept`, and\n`/mark-unlocked-viewed`) through that requester. Do not configure the host SDK\nbase URL with a Loyalty path suffix; the SDK adds product and version routing.\n\n## API\n\n### `LoyaltyModalsProvider`\n\n| Prop           | Type                    | Required | Description                                                 |\n| -------------- | ----------------------- | -------- | ----------------------------------------------------------- |\n| `client`       | `LoyaltyApiClient`      | yes      | Reads status and records accept / view actions.             |\n| `clubUrl`      | `string`                | yes      | Destination used for the default hard redirect to the club. |\n| `onAccessClub` | `() => void`            | no       | Navigation override (see below).                            |\n| `tracking`     | `LoyaltyModalsTracking` | no       | Optional modal interaction callback API owned by the host.  |\n\n### `LoyaltyModals`\n\n| Prop      | Type                                         | Default | Description                                              |\n| --------- | -------------------------------------------- | ------- | -------------------------------------------------------- |\n| `enabled` | `boolean`                                    | `true`  | When `false`, skips the status request and renders null. |\n| `onError` | `(error: unknown, traceId?: string) => void` | —       | Called for status and mutation failures.                 |\n\n### `createLoyaltyClient(base)`\n\nBuilds a `LoyaltyApiClient` from a requester exposing\n`request(endpoint, { method, data })` (e.g. the `@akad/sdk` base), targeting the\nloyalty-program endpoints.\n\nThe mutation requests include a `tracking` config so hosts using\n`@akad/sdk` 2.x + `@akad/data-owl` 2.x emit `Request Completed` events through\nthe SDK tracking pipeline. Each declares a stable, normalized `resource`\n(`/loyalty/program/accept`, `/loyalty/program/mark-unlocked-viewed`); the\nrequired event context (`product`, `productCategory`, `squadId`, `source`,\n`businessFlow`) is inherited from the host SDK instance's `defaultTracking`.\nThey also include business props — `accepted` for `/accept`; `action` and\n`levelId` for `/mark-unlocked-viewed` — which data-owl passes through as\nsnake_case properties (`levelId` → `level_id`). The read-only status request\n(`GET /status`) is not tracked.\nNo analytics package is imported by `@akad/loyalty-modals`.\n\n## Navigation\n\nBoth the invite **accept** button and the level-unlocked **access club** button\nnavigate to the club on success:\n\n- If `onAccessClub` is provided, it is called (use this for SPA routing).\n- Otherwise the package does a hard redirect via `window.location.assign(clubUrl)`.\n\nNavigation only runs after the underlying call succeeds; failures are surfaced\nthrough `onError` and (for invite accept) an inline retry message.\n\n## Tracking\n\nThe package does not import analytics libraries. To track modal interactions,\npass `tracking.onEvent` and map the semantic event to your host app analytics\ncontract.\n\nWith the `@akad/data-owl` 2.x taxonomy, the modal actions become\n`Element Clicked` events and `statusLoaded` becomes a `Request Completed` one.\n\nThere are no impression events: the modals are blocking, so every display ends\nin one of the tracked actions — the buttons, the `Esc` key and a backdrop click\nall emit the secondary action. Request failures are not emitted either; the SDK\nalready reports them as `Request Completed` with `success: false`.\n\nThe host app owns the full payload (fixed context + page context):\n\n```tsx\nimport {\n  ElementType,\n  EventHandler,\n  HttpMethod,\n  pushClickEvent,\n  pushRequestEvent,\n} from '@akad/data-owl';\n\nimport { LOYALTY_TRACKING_CONTEXT } from '@/services/tracking';\n\nconst CLICKED_ELEMENTS = {\n  inviteAccepted: {\n    elementName: 'clube_convite_aceitar',\n    elementType: ElementType.Button,\n  },\n  inviteDeferred: {\n    elementName: 'clube_convite_recusar',\n    elementType: ElementType.Button,\n  },\n  levelUnlockedAccessedClub: {\n    elementName: 'clube_nivel_acessar_clube',\n    elementType: ElementType.Button,\n  },\n  levelUnlockedClosed: {\n    elementName: 'clube_nivel_fechar',\n    elementType: ElementType.Button,\n  },\n} as const;\n\n<LoyaltyModalsProvider\n  client={client}\n  clubUrl=\"https://club.akad.com.br\"\n  tracking={{\n    onEvent: (event) => {\n      if (event.type === 'statusLoaded') {\n        pushRequestEvent(`${LOYALTY_API_URL}/status`, {\n          ...LOYALTY_TRACKING_CONTEXT,\n          durationMs: event.durationMs,\n          handler: EventHandler.DataOwl,\n          loyaltyStatus: event.status, // emitted as loyalty_status\n          method: HttpMethod.Get,\n          resource: '/loyalty/program/status',\n          statusCode: 200, // only successful loads reach here\n          success: true,\n        });\n        return;\n      }\n\n      const element = CLICKED_ELEMENTS[event.type];\n\n      pushClickEvent({\n        ...LOYALTY_TRACKING_CONTEXT, // product, productCategory, squadId, source/customSource, businessFlow\n        ...element,\n        elementSection: 'loyalty_modal',\n        handler: EventHandler.DataOwl,\n        pagePath: window.location.pathname,\n        pageReferrer: document.referrer,\n        pageTitle: document.title,\n        pageUrl: window.location.href,\n        ...('levelId' in event ? { levelId: event.levelId } : {}), // emitted as level_id\n      });\n    },\n  }}\n>\n  <LoyaltyModals />\n</LoyaltyModalsProvider>;\n```\n\n`tracking.onEvent` receives this union:\n\n```ts\ntype LoyaltyModalEvent =\n  | {\n      durationMs?: number;\n      status: 'initial' | 'accepted';\n      type: 'statusLoaded';\n    }\n  | { type: 'inviteAccepted' }\n  | { type: 'inviteDeferred' }\n  | { levelId: string; levelName: string; type: 'levelUnlockedAccessedClub' }\n  | { levelId: string; levelName: string; type: 'levelUnlockedClosed' };\n```\n\n`statusLoaded` fires once per successful status load, including the refetches\nafter accept / view actions, and carries the request latency in `durationMs`. Only successful loads emit it: the endpoint answers\n403 for brokers outside the loyalty group, so an event means the broker is in\nthe group, and `status` says whether they had already accepted the invite\n(`initial` before accepting, `accepted` after) — expect an `initial` event\nfollowed by an `accepted` one when a broker accepts.\n\nThe action events fire when the user invokes the corresponding modal action —\n`inviteDeferred` and `levelUnlockedClosed` also cover `Esc` and backdrop\ndismissals — except `inviteAccepted`, which fires only after the accept request\nsucceeds.\n","readmeFilename":"README.md"}