@auto-topup/web 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -0
- package/dist/index.js +43 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -16
- package/dist/index.mjs.map +1 -1
- package/dist/retailcode.iife.global.js +8 -11
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @auto-topup/web
|
|
2
|
+
|
|
3
|
+
Auto Topup subscription widget for browsers and any WebView.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @auto-topup/web
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or via CDN (no install):
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<script src="https://cdn.jsdelivr.net/npm/@auto-topup/web@0.3.0/dist/retailcode.iife.global.js"></script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage — React / Next.js / Vite
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { RetailcodeTopup } from '@auto-topup/web';
|
|
21
|
+
|
|
22
|
+
RetailcodeTopup.create({
|
|
23
|
+
publicKey: 'pk_live_xxxx',
|
|
24
|
+
msisdn: user.phone,
|
|
25
|
+
container: '#topup-widget',
|
|
26
|
+
theme: { accent: '#0057FF' },
|
|
27
|
+
onSuccess: () => router.push('/success'),
|
|
28
|
+
onClose: () => router.push('/dashboard'),
|
|
29
|
+
}).mount();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Next.js
|
|
33
|
+
|
|
34
|
+
Use `useState` + `useEffect` so the container div is in the DOM before `.mount()` runs:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
'use client';
|
|
38
|
+
import { useEffect, useState } from 'react';
|
|
39
|
+
import { useRouter } from 'next/navigation';
|
|
40
|
+
import { RetailcodeTopup } from '@auto-topup/web';
|
|
41
|
+
|
|
42
|
+
export default function SomePage() {
|
|
43
|
+
const router = useRouter();
|
|
44
|
+
const [widgetOpen, setWidgetOpen] = useState(false);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!widgetOpen) return;
|
|
48
|
+
RetailcodeTopup.create({
|
|
49
|
+
publicKey: 'pk_live_xxxx',
|
|
50
|
+
msisdn: '08012345678',
|
|
51
|
+
container: '#topup-widget',
|
|
52
|
+
onClose: () => setWidgetOpen(false),
|
|
53
|
+
onSuccess: () => setWidgetOpen(false),
|
|
54
|
+
}).mount();
|
|
55
|
+
}, [widgetOpen]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<>
|
|
59
|
+
<button onClick={() => setWidgetOpen(true)}>Open Topup</button>
|
|
60
|
+
|
|
61
|
+
{widgetOpen && (
|
|
62
|
+
<div id="topup-widget" style={{ position: 'fixed', inset: 0, zIndex: 9999 }} />
|
|
63
|
+
)}
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Usage — Plain HTML / CDN
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<script src="https://cdn.jsdelivr.net/npm/@auto-topup/web@0.3.0/dist/retailcode.iife.global.js"></script>
|
|
73
|
+
|
|
74
|
+
<div id="topup-widget"></div>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
RetailcodeSDK.RetailcodeTopup.create({
|
|
78
|
+
publicKey: 'pk_live_xxxx',
|
|
79
|
+
msisdn: '08012345678',
|
|
80
|
+
container: '#topup-widget',
|
|
81
|
+
theme: { accent: '#0057FF' },
|
|
82
|
+
onSuccess: function(r) { console.log('subscribed', r); },
|
|
83
|
+
onClose: function() { window.location.href = '/'; },
|
|
84
|
+
}).mount();
|
|
85
|
+
</script>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Options
|
|
89
|
+
|
|
90
|
+
| Option | Type | Required | Description |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `publicKey` | `string` | Yes | Your Retailcode public key |
|
|
93
|
+
| `msisdn` | `string` | Yes | Subscriber phone number |
|
|
94
|
+
| `container` | `string` | Yes | CSS selector for the mount element |
|
|
95
|
+
| `theme.accent` | `string` | No | Hex colour for buttons and accents (default `#0057FF`) |
|
|
96
|
+
| `theme.fontFamily` | `string` | No | Font family override (default `'DM Sans', system-ui`) |
|
|
97
|
+
| `onSuccess` | `() => void` | No | Called after a successful subscription |
|
|
98
|
+
| `onClose` | `() => void` | No | Called when the user dismisses the widget |
|
|
99
|
+
|
|
100
|
+
> **Note:** `onSuccess` and `onClose` are mutually exclusive — only one fires per session.
|
|
101
|
+
|
|
102
|
+
## Full documentation
|
|
103
|
+
|
|
104
|
+
See the [main repository README](https://github.com/FBIS-Tech/auto-topup-sdk).
|
package/dist/index.js
CHANGED
|
@@ -219,6 +219,43 @@ function buildStyles(t) {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
// src/widget.ts
|
|
222
|
+
function showTermsDialog(opts) {
|
|
223
|
+
return new Promise((resolve) => {
|
|
224
|
+
const overlay = document.createElement("div");
|
|
225
|
+
overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:99999;padding:16px;box-sizing:border-box;";
|
|
226
|
+
const card = document.createElement("div");
|
|
227
|
+
card.style.cssText = `background:#fff;border-radius:12px;width:100%;max-width:480px;max-height:85vh;display:flex;flex-direction:column;font-family:${opts.fontFamily};`;
|
|
228
|
+
const title = document.createElement("div");
|
|
229
|
+
title.style.cssText = "padding:20px 24px 12px;font-size:18px;font-weight:700;color:#111827;text-align:center;flex-shrink:0;";
|
|
230
|
+
title.textContent = "Terms & Conditions";
|
|
231
|
+
const content = document.createElement("div");
|
|
232
|
+
content.style.cssText = "flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;overscroll-behavior:contain;padding:0 24px 16px;font-size:13px;line-height:1.65;color:#374151;";
|
|
233
|
+
content.innerHTML = opts.html;
|
|
234
|
+
const actions = document.createElement("div");
|
|
235
|
+
actions.style.cssText = "display:flex;gap:12px;padding:16px 24px;flex-shrink:0;border-top:1px solid #F3F4F6;";
|
|
236
|
+
const dismiss = (agreed) => {
|
|
237
|
+
document.body.style.overflow = "";
|
|
238
|
+
document.body.removeChild(overlay);
|
|
239
|
+
resolve(agreed);
|
|
240
|
+
};
|
|
241
|
+
const decline = document.createElement("button");
|
|
242
|
+
decline.style.cssText = "flex:1;padding:12px;border-radius:8px;border:none;background:#9CA3AF;color:#fff;font-size:14px;font-weight:600;cursor:pointer;";
|
|
243
|
+
decline.textContent = "Decline";
|
|
244
|
+
decline.onclick = () => dismiss(false);
|
|
245
|
+
const agree = document.createElement("button");
|
|
246
|
+
agree.style.cssText = `flex:2;padding:12px;border-radius:8px;border:none;background:${opts.accent};color:#fff;font-size:14px;font-weight:600;cursor:pointer;`;
|
|
247
|
+
agree.textContent = "I Agree & Continue";
|
|
248
|
+
agree.onclick = () => dismiss(true);
|
|
249
|
+
actions.appendChild(decline);
|
|
250
|
+
actions.appendChild(agree);
|
|
251
|
+
card.appendChild(title);
|
|
252
|
+
card.appendChild(content);
|
|
253
|
+
card.appendChild(actions);
|
|
254
|
+
overlay.appendChild(card);
|
|
255
|
+
document.body.style.overflow = "hidden";
|
|
256
|
+
document.body.appendChild(overlay);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
222
259
|
var TopupWidget = class {
|
|
223
260
|
constructor(opts) {
|
|
224
261
|
this.opts = opts;
|
|
@@ -257,7 +294,6 @@ var TopupWidget = class {
|
|
|
257
294
|
return;
|
|
258
295
|
}
|
|
259
296
|
shadow.innerHTML = "";
|
|
260
|
-
await loadSwal();
|
|
261
297
|
const {
|
|
262
298
|
name: prefilledName = "",
|
|
263
299
|
subscribedAirtime = false,
|
|
@@ -273,26 +309,17 @@ var TopupWidget = class {
|
|
|
273
309
|
const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;
|
|
274
310
|
const isBrandNew = !subscribedAirtime && !subscribedData;
|
|
275
311
|
if (isBrandNew && terms) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
${typeof terms === "string" ? terms.replace(/\n/g, "<br>") : ""}
|
|
281
|
-
</div>`,
|
|
282
|
-
confirmButtonText: "I Agree & Continue",
|
|
283
|
-
confirmButtonColor: accent,
|
|
284
|
-
showCancelButton: true,
|
|
285
|
-
cancelButtonText: "Decline",
|
|
286
|
-
cancelButtonColor: "#9CA3AF",
|
|
287
|
-
allowOutsideClick: false,
|
|
288
|
-
allowEscapeKey: false,
|
|
289
|
-
reverseButtons: true
|
|
312
|
+
const agreed = await showTermsDialog({
|
|
313
|
+
html: typeof terms === "string" ? terms.replace(/\n/g, "<br>") : "",
|
|
314
|
+
accent,
|
|
315
|
+
fontFamily
|
|
290
316
|
});
|
|
291
|
-
if (!
|
|
317
|
+
if (!agreed) {
|
|
292
318
|
closeWebview(onClose);
|
|
293
319
|
return;
|
|
294
320
|
}
|
|
295
321
|
}
|
|
322
|
+
await loadSwal();
|
|
296
323
|
const airtimeOptions = Object.entries(airtimethresholds).map(([k, id]) => `<option value="${id}">Below \u20A6${k}</option>`).join("");
|
|
297
324
|
const dataOptions = Object.entries(dataThresholds).map(([k, id]) => `<option value="${id}">Below ${k}</option>`).join("");
|
|
298
325
|
const dataPlanOptions = dataPlans.map((p) => `<option value="${p.productId}">${p.allowance} \u2014 \u20A6${p.price}</option>`).join("");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/widget.ts","../src/dialog.ts","../src/webview.ts","../src/styles.ts"],"sourcesContent":["import { resolveAccent } from '@auto-topup/core';\nimport { TopupWidget } from './widget.js';\nimport type { TopupCallbacks } from '@auto-topup/core';\n\nexport type { TopupCallbacks };\nexport { TopupWidget };\n\nexport interface CreateConfig extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\ntheme?: { accent?: string; fontFamily?: string };\n}\n\nexport const RetailcodeTopup = {\n create(config: CreateConfig) {\n const widget = new TopupWidget({\n ...config,\n theme: {\n ...config.theme,\n accent: resolveAccent(config.theme?.accent),\n },\n });\n return {\n mount: () => widget.mount(),\n };\n },\n};\n","import {\n RetailcodeApiClient,\n RetailcodeApiError,\n TopupApiConfig,\n TopupCallbacks,\n resolveAccent,\n formatPhone,\n isValidMsisdn,\n isValidAmount,\n} from '@auto-topup/core';\nimport { loadSwal, swal } from './dialog.js';\nimport { closeWebview, updateUrlStatus } from './webview.js';\nimport { buildTokens, buildStyles } from './styles.js';\n\ninterface WidgetOptions extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\n theme?: { accent?: string; fontFamily?: string };\n}\n\nexport class TopupWidget {\n private readonly client: RetailcodeApiClient;\n private readonly opts: WidgetOptions;\n\n constructor(opts: WidgetOptions) {\n this.opts = opts;\n this.client = new RetailcodeApiClient(opts.publicKey);\n }\n\n async mount(): Promise<void> {\n const { msisdn, container, onClose, theme = {} } = this.opts;\n\n const target = document.querySelector(container);\n if (!target) return;\n\n const accent = resolveAccent(theme.accent);\n const fontFamily = theme.fontFamily ?? \"'DM Sans', system-ui, sans-serif\";\n const tokens = buildTokens(accent, fontFamily);\n\n let shadow: ShadowRoot | null = null;\n\n const showInitError = (message: string) => {\n const html = `\n <div style=\"font-family:${fontFamily};padding:24px;border:1.5px solid #FCA5A5;background:#FEF2F2;border-radius:8px;color:#991B1B;text-align:center;max-width:440px;margin:40px auto;\">\n <div style=\"font-weight:700;font-size:15px;margin-bottom:4px;\">SDK failed to initialize</div>\n <div style=\"font-size:13px;opacity:0.8;\">${message}</div>\n </div>`;\n if (shadow) shadow.innerHTML = html;\n else (target as HTMLElement).innerHTML = html;\n };\n\n if (!isValidMsisdn(msisdn)) {\n showInitError('Subscriber phone number is not valid.');\n return;\n }\n\n shadow = target.attachShadow({ mode: 'open' });\n this.renderSpinner(shadow, tokens);\n\n let cfg: TopupApiConfig;\n try {\n cfg = await this.client.fetchConfig(msisdn);\n } catch (e) {\n const msg =\n e instanceof RetailcodeApiError ? e.message : 'Could not connect to the activation server.';\n showInitError(msg);\n return;\n }\n\n shadow.innerHTML = '';\n await loadSwal();\n\n const {\n name: prefilledName = '',\n subscribedAirtime = false,\n subscribedData = false,\n airtimethresholds = {},\n airtimeMin = 0,\n airtimeMax = 10000,\n dataThresholds = {},\n dataPlans = [],\n terms = null,\n } = cfg;\n\n const isFullSubscriber = subscribedAirtime && subscribedData;\n const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;\n const isBrandNew = !subscribedAirtime && !subscribedData;\n\n // Terms gate for brand-new users\n if (isBrandNew && terms) {\n const result = await swal({\n title: 'Terms & Conditions',\n html: `\n <div style=\"text-align:left;font-size:13px;line-height:1.65;color:#374151;max-height:340px;overflow-y:auto;padding-right:4px;font-family:${fontFamily};\">\n ${typeof terms === 'string' ? terms.replace(/\\n/g, '<br>') : ''}\n </div>`,\n confirmButtonText: 'I Agree & Continue',\n confirmButtonColor: accent,\n showCancelButton: true,\n cancelButtonText: 'Decline',\n cancelButtonColor: '#9CA3AF',\n allowOutsideClick: false,\n allowEscapeKey: false,\n reverseButtons: true,\n });\n if (!result.isConfirmed) {\n closeWebview(onClose);\n return;\n }\n }\n\n // Build option HTML strings\n const airtimeOptions = Object.entries(airtimethresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ₦${k}</option>`)\n .join('');\n const dataOptions = Object.entries(dataThresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ${k}</option>`)\n .join('');\n const dataPlanOptions = dataPlans\n .map(p => `<option value=\"${p.productId}\">${p.allowance} — ₦${p.price}</option>`)\n .join('');\n\n const modal = document.createElement('div');\n modal.className = 'rc-modal';\n modal.innerHTML = this.buildModalHtml({\n tokens,\n msisdn,\n prefilledName,\n airtimeMin,\n airtimeMax,\n airtimeOptions,\n dataOptions,\n dataPlanOptions,\n });\n\n shadow.appendChild(modal);\n\n // ── Refs ────────────────────────────────────────────────────────────────\n const $ = <T extends Element = HTMLElement>(id: string) =>\n shadow!.getElementById(id) as T | null;\n\n const typeSelect = $<HTMLSelectElement>('rc-type-select')!;\n const airtimeSec = $('section-airtime')!;\n const dataSec = $('section-data')!;\n const titleText = $('rc-main-title')!;\n const descText = $('rc-desc')!;\n const submitBtn = $<HTMLButtonElement>('rc-submit')!;\n const msisdnField = $('field-msisdn')!;\n const depLinkWrap = $('rc-dependent-link-wrap')!;\n const depControls = $('section-dependent-controls')!;\n const typeSelectorGrp = $('group-type-selector')!;\n const beneficiaryRow = $('row-beneficiary')!;\n const spendingRow = $('row-spending')!;\n const airtimeMaxField = $('field-airtime-max')!;\n const dataMaxField = $('field-data-max')!;\n\n let forceDependentView = false;\n\n const refreshUI = () => {\n const isDep = isFullSubscriber || forceDependentView;\n const mode = typeSelect.value;\n\n if (isDep) {\n titleText.innerText = 'Add Dependent';\n descText.innerText = 'Configure subscription for someone else';\n msisdnField.classList.remove('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr 1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.remove('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n airtimeMaxField.classList.toggle('hidden', mode === 'data');\n dataMaxField.classList.toggle('hidden', mode === 'airtime');\n spendingRow.style.gridTemplateColumns = mode === 'both' ? '1fr 1fr' : '1fr';\n } else if (isPartialSubscriber) {\n titleText.innerText = 'Complete Profile';\n descText.innerText = 'Finish your Auto Topup subscription';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.add('hidden');\n depLinkWrap.classList.remove('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', subscribedAirtime);\n dataSec.classList.toggle('hidden', subscribedData);\n } else {\n titleText.innerText = 'Auto Topup Subscription';\n descText.innerText = 'Automate your airtime & data recharge';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n }\n };\n\n refreshUI();\n\n // ── Event wiring ────────────────────────────────────────────────────────\n const unmount = () => { shadow!.innerHTML = ''; };\n\n typeSelect.addEventListener('change', refreshUI);\n $('rc-close')!.addEventListener('click', () => closeWebview(onClose, unmount));\n\n $('rc-switch-to-dep')!.addEventListener('click', (e) => {\n e.preventDefault();\n forceDependentView = true;\n ($<HTMLInputElement>('rc-name'))!.value = '';\n refreshUI();\n });\n\n $<HTMLInputElement>('rc-name')!.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/[^a-zA-Z\\s]/g, '');\n });\n\n ['rc-msisdn-input', 'airtimeTopupValue', 'rc-airtime-max', 'rc-data-max'].forEach(id => {\n $<HTMLInputElement>(id)?.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/\\D/g, '');\n });\n });\n\n shadow.querySelectorAll<HTMLInputElement | HTMLSelectElement>('.rc-field input, .rc-field select')\n .forEach(el => {\n const sync = () => el.closest('.rc-field')!.classList.toggle('has-val', el.value !== '');\n el.addEventListener('input', sync);\n el.addEventListener('change', sync);\n sync();\n });\n\n // ── Submit ──────────────────────────────────────────────────────────────\n submitBtn.addEventListener('click', async () => {\n const getVal = (id: string) =>\n ($<HTMLInputElement>(id))?.value.trim() ?? '';\n\n const isDep = isFullSubscriber || forceDependentView;\n const beneficiary = isDep ? getVal('rc-msisdn-input') : msisdn;\n\n if (!airtimeSec.classList.contains('hidden')) {\n if (!isValidAmount(getVal('airtimeTopupValue'), airtimeMin, airtimeMax)) {\n swal({ icon: 'warning', title: 'Invalid Amount', text: `Enter ₦${airtimeMin} – ₦${airtimeMax}.`, confirmButtonColor: accent });\n return;\n }\n }\n if (isDep && !beneficiary) {\n swal({ icon: 'warning', title: 'Wait!', text: 'Enter the dependent phone number.', confirmButtonColor: accent });\n return;\n }\n\n submitBtn.disabled = true;\n submitBtn.innerText = 'Processing…';\n\n const common = { network: 'MTN', msisdn: beneficiary, name: getVal('rc-name') };\n\n try {\n if (!airtimeSec.classList.contains('hidden')) {\n await this.client.subscribeAirtime({\n ...common,\n airtimeThresholdId: getVal('airtimeThresholdId'),\n airtimeTopupValue: getVal('airtimeTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-airtime-max') }),\n });\n }\n if (!dataSec.classList.contains('hidden')) {\n await this.client.subscribeData({\n ...common,\n dataThresholdId: getVal('dataThresholdId'),\n dataTopupValue: getVal('dataTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-data-max') }),\n });\n }\n\n updateUrlStatus('successful');\n this.opts.onSuccess?.({ success: true });\n // Use didClose so we notify the native host AFTER SweetAlert2 finishes\n // its close animation — prevents the black-screen flash that occurs when\n // Flutter/iOS dismisses the sheet while the backdrop is still fading out.\n swal({\n icon: 'success',\n title: 'Subscription Active!',\n text: 'Your auto top-up is now enabled.',\n confirmButtonColor: accent,\n didClose: () => closeWebview(onClose, unmount, true),\n });\n } catch (err) {\n updateUrlStatus('failed');\n swal({ icon: 'error', title: 'Oops!', text: (err as Error).message, confirmButtonColor: accent });\n submitBtn.disabled = false;\n submitBtn.innerText = 'Activate Subscription';\n }\n });\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private renderSpinner(shadow: ShadowRoot, tokens: ReturnType<typeof buildTokens>): void {\n const el = document.createElement('div');\n el.innerHTML = `\n <style>\n :host { display:flex; align-items:center; justify-content:center; min-height:100vh; background:#fff; }\n @media (min-width:560px) { :host { background:rgba(0,0,0,.50); } }\n ${buildStyles(tokens).match(/\\.rc-spinner-ring[\\s\\S]*?@keyframes rc-spin[\\s\\S]*?}/)?.[0] ?? ''}\n </style>\n <div class=\"rc-spinner-ring\"></div>`;\n shadow.appendChild(el);\n }\n\n private buildModalHtml(p: {\n tokens: ReturnType<typeof buildTokens>;\n msisdn: string;\n prefilledName: string;\n airtimeMin: number;\n airtimeMax: number;\n airtimeOptions: string;\n dataOptions: string;\n dataPlanOptions: string;\n }): string {\n return `\n <style>${buildStyles(p.tokens)}</style>\n\n <div class=\"rc-header\">\n <div class=\"rc-header-top\">\n <div class=\"rc-title\">\n <h2 id=\"rc-main-title\">Auto Topup Subscription</h2>\n <p id=\"rc-desc\">Automate your airtime & data recharge</p>\n </div>\n <button class=\"rc-close-btn\" id=\"rc-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"rc-account-bar\">\n <div class=\"rc-dot\"></div>\n <div>\n <span class=\"rc-acc-label\">Your Phone Number</span>\n <span class=\"rc-acc-no\">${formatPhone(p.msisdn)}</span>\n </div>\n </div>\n </div>\n\n <div class=\"rc-content\">\n <div class=\"rc-group\">\n <p class=\"rc-group-title\">Registration Details</p>\n <div class=\"rc-row\" id=\"row-beneficiary\">\n <div class=\"rc-field\" id=\"field-name\">\n <input type=\"text\" id=\"rc-name\" placeholder=\" \" value=\"${p.prefilledName}\" autocomplete=\"name\">\n <label>Full Name</label>\n </div>\n <div class=\"rc-field hidden\" id=\"field-msisdn\">\n <input type=\"tel\" id=\"rc-msisdn-input\" placeholder=\" \" autocomplete=\"tel\">\n <label>Phone Number</label>\n </div>\n </div>\n </div>\n\n <div class=\"rc-group\" id=\"group-type-selector\">\n <p class=\"rc-group-title\">Subscription Type</p>\n <div class=\"rc-field has-val\">\n <select id=\"rc-type-select\">\n <option value=\"airtime\">Airtime Only</option>\n <option value=\"data\">Data Only</option>\n <option value=\"both\">Both Airtime & Data</option>\n </select>\n <label>Automation Mode</label>\n </div>\n </div>\n\n <hr class=\"rc-divider\">\n\n <div id=\"section-airtime\" class=\"section-animate\">\n <p class=\"rc-group-title\">Airtime Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"airtimeThresholdId\">${p.airtimeOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field\">\n <input type=\"tel\" id=\"airtimeTopupValue\" placeholder=\" \">\n <label>Amount (₦)</label>\n <small>₦${p.airtimeMin} – ₦${p.airtimeMax}</small>\n </div>\n </div>\n </div>\n\n <div id=\"section-data\" class=\"hidden section-animate\">\n <p class=\"rc-group-title\">Data Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"dataThresholdId\">${p.dataOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field has-val\">\n <select id=\"dataTopupValue\">${p.dataPlanOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Select Plan</label>\n </div>\n </div>\n </div>\n\n <div id=\"section-dependent-controls\" class=\"hidden\">\n <hr class=\"rc-divider\">\n <p class=\"rc-group-title\">Spending Controls</p>\n <div class=\"rc-row\" id=\"row-spending\">\n <div class=\"rc-field\" id=\"field-airtime-max\">\n <input type=\"tel\" id=\"rc-airtime-max\" placeholder=\" \">\n <label>Airtime Limit (₦)</label>\n </div>\n <div class=\"rc-field\" id=\"field-data-max\">\n <input type=\"tel\" id=\"rc-data-max\" placeholder=\" \">\n <label>Data Limit (₦)</label>\n </div>\n </div>\n </div>\n\n <button class=\"rc-submit-btn\" id=\"rc-submit\">Activate Subscription</button>\n\n <div id=\"rc-dependent-link-wrap\" class=\"rc-link-wrap hidden\">\n Or <a id=\"rc-switch-to-dep\">add a dependent instead</a>\n </div>\n </div>\n\n <div class=\"rc-footer\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"/>\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>\n </svg>\n Secured by <strong> Retailcode</strong>\n </div>\n `;\n }\n}\n","// Lazy-loads SweetAlert2 from CDN on first call.\n// The Swal reference is typed loosely so we don't need @types/sweetalert2.\n\ndeclare global {\n interface Window {\n Swal: SwalStatic;\n }\n}\n\ninterface SwalStatic {\n fire(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }>;\n}\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function loadSwal(): Promise<void> {\n if (window.Swal) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css';\n document.head.appendChild(link);\n\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11';\n script.onload = () => resolve();\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\nexport function swal(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }> {\n return window.Swal.fire(opts);\n}\n","// Bridges for closing the widget across all host environments.\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: { postMessage(msg: string): void };\n webkit?: { messageHandlers?: { retailcode?: { postMessage(msg: unknown): void } } };\n Android?: { close?(): void };\n RetailcodeFlutter?: { postMessage(msg: string): void }; // Flutter JavascriptChannel\n }\n}\n\nexport function updateUrlStatus(status: 'successful' | 'failed'): void {\n const url = new URL(window.location.href);\n url.searchParams.set('status', status);\n window.history.replaceState({}, '', url);\n}\n\nexport function closeWebview(\n onClose?: (r: { closed: true }) => void,\n unmount?: () => void,\n success = false,\n): void {\n const url = new URL(window.location.href);\n url.searchParams.set('isClose', 'true');\n window.history.replaceState({}, '', url);\n\n const isNative = !!(\n window.ReactNativeWebView ||\n window.RetailcodeFlutter ||\n window.webkit?.messageHandlers?.retailcode ||\n window.Android?.close\n );\n\n // In native WebView contexts the host app dismisses the whole view, so\n // clearing the DOM first causes a black-screen flash. Only unmount for\n // plain browser usage where there is no native dismiss.\n if (!isNative) unmount?.();\n\n onClose?.({ closed: true });\n\n // Include success flag so native apps know whether to fire onSuccess\n // before dismissing — this way the dialog is always fully visible first.\n const msg = JSON.stringify({ action: 'close', success });\n\n if (window.ReactNativeWebView) {\n window.ReactNativeWebView.postMessage(msg);\n } else if (window.RetailcodeFlutter) {\n window.RetailcodeFlutter.postMessage(msg);\n } else if (window.webkit?.messageHandlers?.retailcode) {\n window.webkit.messageHandlers.retailcode.postMessage({ action: 'close', success });\n } else if (window.Android?.close) {\n window.Android.close();\n }\n}\n","import { mix, rgba } from '@auto-topup/core';\n\nexport interface StyleTokens {\n accent: string;\n accentHover: string;\n accentFocus: string;\n accentShadow: string;\n fontFamily: string;\n}\n\nexport function buildTokens(accent: string, fontFamily: string): StyleTokens {\n return {\n accent,\n accentHover: mix(accent, -10),\n accentFocus: rgba(accent, 0.15),\n accentShadow: rgba(accent, 0.25),\n fontFamily,\n };\n}\n\nexport function buildStyles(t: StyleTokens): string {\n return `\n @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :host {\n display: flex;\n justify-content: center;\n min-height: 100vh;\n font-family: ${t.fontFamily};\n background: #FFFFFF;\n }\n @media (min-width: 560px) {\n :host { background: rgba(0,0,0,.50); align-items: center; padding: 20px; }\n }\n\n .rc-modal {\n width: 100%; max-width: 440px; background: #FFFFFF; overflow: hidden;\n animation: modalSlide .35s cubic-bezier(.22,1,.36,1) both;\n border-radius: 0; box-shadow: none;\n }\n @media (min-width: 560px) {\n .rc-modal {\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,.06), 0 12px 32px rgba(0,0,0,.12), 0 32px 64px rgba(0,0,0,.08);\n }\n }\n @keyframes modalSlide { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:none; } }\n\n .rc-header { padding: 24px 20px 14px; }\n .rc-header-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }\n .rc-title h2 { font-size: 19px; font-weight: 700; color: #111827; letter-spacing: -.3px; }\n .rc-title p { font-size: 13px; color: #6B7280; margin-top: 3px; line-height: 1.4; }\n\n .rc-close-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; background: #F3F4F6; border: none;\n border-radius: 6px; color: #6B7280; cursor: pointer; transition: background .15s; flex-shrink: 0;\n }\n .rc-close-btn:hover { background: #E5E7EB; }\n\n .rc-account-bar {\n display: flex; align-items: center; background: ${t.accent};\n padding: 10px 14px; border-radius: 6px; color: #fff;\n }\n .rc-dot { width: 8px; height: 8px; background: #4ADE80; border-radius: 50%; margin-right: 10px; box-shadow: 0 0 0 2px rgba(74,222,128,.3); flex-shrink: 0; }\n .rc-acc-label { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .65; display: block; margin-bottom: 1px; }\n .rc-acc-no { font-size: 14px; font-weight: 700; letter-spacing: .03em; }\n\n .rc-content { padding: 20px 20px 16px; }\n .rc-group { margin-bottom: 14px; }\n .rc-group-title { font-size: 10px; font-weight: 700; color: #9CA3AF; text-transform: uppercase; letter-spacing: .09em; margin-bottom: 10px; }\n\n .rc-field { position: relative; margin-bottom: 10px; }\n .rc-field:last-child { margin-bottom: 0; }\n .rc-field label { position: absolute; left: 12px; top: 16px; font-size: 13.5px; color: #9CA3AF; transition: all .16s cubic-bezier(.4,0,.2,1); pointer-events: none; transform-origin: left top; }\n .rc-field input, .rc-field select {\n width: 100%; height: 52px; padding: 20px 12px 6px;\n background: #F9FAFB; border: 1.5px solid #E5E7EB; border-radius: 6px;\n font-family: inherit; font-size: 14px; font-weight: 500; color: #111827;\n outline: none; transition: border-color .18s, box-shadow .18s, background .18s;\n appearance: none; -webkit-appearance: none;\n }\n .rc-field select {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px; cursor: pointer;\n }\n .rc-field input:focus, .rc-field select:focus {\n border-color: ${t.accent}; background: #FFFFFF; box-shadow: 0 0 0 3px ${t.accentFocus};\n }\n .rc-field.has-val label, .rc-field input:focus ~ label, .rc-field select:focus ~ label {\n top: 7px; font-size: 10.5px; font-weight: 700; color: ${t.accent};\n }\n .rc-field small { display: block; margin-top: 4px; font-size: 11px; color: #9CA3AF; }\n\n .rc-row { display: grid; gap: 10px; margin-bottom: 10px; }\n .rc-row .rc-field { margin-bottom: 0; }\n .rc-divider { height: 1px; background: #F3F4F6; margin: 16px 0; border: none; }\n .section-animate { animation: fadeIn .25s ease-out; }\n @keyframes fadeIn { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }\n\n .rc-link-wrap { margin-top: 12px; text-align: center; font-size: 13px; color: #9CA3AF; }\n .rc-link-wrap a { color: ${t.accent}; text-decoration: none; font-weight: 600; cursor: pointer; }\n .rc-link-wrap a:hover { text-decoration: underline; }\n\n .rc-submit-btn {\n width: 100%; height: 52px; background: ${t.accent}; color: #FFFFFF; border: none;\n border-radius: 6px; font-family: inherit; font-size: 15px; font-weight: 700;\n cursor: pointer; letter-spacing: .01em; margin-top: 6px;\n box-shadow: 0 4px 12px ${t.accentShadow};\n transition: background .18s, transform .1s, box-shadow .18s;\n }\n .rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${rgba(t.accent, 0.35)}; }\n .rc-submit-btn:active:not(:disabled) { transform: translateY(0); }\n .rc-submit-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }\n\n .rc-spinner-ring {\n width: 48px; height: 48px;\n border: 4px solid ${rgba(t.accent, 0.18)};\n border-top-color: ${t.accent};\n border-radius: 50%;\n animation: rc-spin .75s linear infinite;\n }\n @keyframes rc-spin { to { transform: rotate(360deg); } }\n\n .rc-footer {\n text-align: center; padding: 0 20px 18px; font-size: 11px; color: #D1D5DB;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n }\n .rc-footer svg { opacity: .5; }\n .hidden { display: none !important; }\n `;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,eAA8B;;;ACA9B,IAAAC,eASO;;;ACIP,IAAI,cAAoC;AAEjC,SAAS,WAA0B;AACxC,MAAI,OAAO,KAAM,QAAO,QAAQ,QAAQ;AACxC,MAAI,YAAa,QAAO;AAExB,gBAAc,IAAI,QAAc,CAAC,YAAY;AAC3C,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,aAAS,KAAK,YAAY,IAAI;AAE9B,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,MAAM;AACb,WAAO,SAAS,MAAM,QAAQ;AAC9B,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AAED,SAAO;AACT;AAEO,SAAS,KAAK,MAAkE;AACrF,SAAO,OAAO,KAAK,KAAK,IAAI;AAC9B;;;ACzBO,SAAS,gBAAgB,QAAuC;AACrE,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,MAAI,aAAa,IAAI,UAAU,MAAM;AACrC,SAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,GAAG;AACzC;AAEO,SAAS,aACd,SACA,SACA,UAAU,OACJ;AArBR;AAsBE,QAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,MAAI,aAAa,IAAI,WAAW,MAAM;AACtC,SAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,GAAG;AAEvC,QAAM,WAAW,CAAC,EAChB,OAAO,sBACP,OAAO,uBACP,kBAAO,WAAP,mBAAe,oBAAf,mBAAgC,iBAChC,YAAO,YAAP,mBAAgB;AAMlB,MAAI,CAAC,SAAU;AAEf,qCAAU,EAAE,QAAQ,KAAK;AAIzB,QAAM,MAAM,KAAK,UAAU,EAAE,QAAQ,SAAS,QAAQ,CAAC;AAEvD,MAAI,OAAO,oBAAoB;AAC7B,WAAO,mBAAmB,YAAY,GAAG;AAAA,EAC3C,WAAW,OAAO,mBAAmB;AACnC,WAAO,kBAAkB,YAAY,GAAG;AAAA,EAC1C,YAAW,kBAAO,WAAP,mBAAe,oBAAf,mBAAgC,YAAY;AACrD,WAAO,OAAO,gBAAgB,WAAW,YAAY,EAAE,QAAQ,SAAS,QAAQ,CAAC;AAAA,EACnF,YAAW,YAAO,YAAP,mBAAgB,OAAO;AAChC,WAAO,QAAQ,MAAM;AAAA,EACvB;AACF;;;ACrDA,kBAA0B;AAUnB,SAAS,YAAY,QAAgB,YAAiC;AAC3E,SAAO;AAAA,IACL;AAAA,IACA,iBAAa,iBAAI,QAAQ,GAAG;AAAA,IAC5B,iBAAa,kBAAK,QAAQ,IAAI;AAAA,IAC9B,kBAAc,kBAAK,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AACF;AAEO,SAAS,YAAY,GAAwB;AAClD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAQY,EAAE,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wDAiCuB,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBA0B1C,EAAE,MAAM,gDAAgD,EAAE,WAAW;AAAA;AAAA;AAAA,8DAG7B,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAWvC,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA,+CAIQ,EAAE,MAAM;AAAA;AAAA;AAAA,+BAGxB,EAAE,YAAY;AAAA;AAAA;AAAA,wDAGW,EAAE,WAAW,6DAAyD,kBAAK,EAAE,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMxH,kBAAK,EAAE,QAAQ,IAAI,CAAC;AAAA,0BACpB,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAalC;;;AH/GO,IAAM,cAAN,MAAkB;AAAA,EAIvB,YAAY,MAAqB;AAC/B,SAAK,OAAO;AACZ,SAAK,SAAS,IAAI,iCAAoB,KAAK,SAAS;AAAA,EACtD;AAAA,EAEA,MAAM,QAAuB;AA9B/B;AA+BI,UAAM,EAAE,QAAQ,WAAW,SAAS,QAAQ,CAAC,EAAE,IAAI,KAAK;AAExD,UAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,QAAI,CAAC,OAAQ;AAEb,UAAM,aAAS,4BAAc,MAAM,MAAM;AACzC,UAAM,cAAa,WAAM,eAAN,YAAoB;AACvC,UAAM,SAAS,YAAY,QAAQ,UAAU;AAE7C,QAAI,SAA4B;AAEhC,UAAM,gBAAgB,CAAC,YAAoB;AACzC,YAAM,OAAO;AAAA,kCACe,UAAU;AAAA;AAAA,qDAES,OAAO;AAAA;AAEtD,UAAI,OAAQ,QAAO,YAAY;AAAA,UAC1B,CAAC,OAAuB,YAAY;AAAA,IAC3C;AAEA,QAAI,KAAC,4BAAc,MAAM,GAAG;AAC1B,oBAAc,uCAAuC;AACrD;AAAA,IACF;AAEA,aAAS,OAAO,aAAa,EAAE,MAAM,OAAO,CAAC;AAC7C,SAAK,cAAc,QAAQ,MAAM;AAEjC,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK,OAAO,YAAY,MAAM;AAAA,IAC5C,SAAS,GAAG;AACV,YAAM,MACJ,aAAa,kCAAqB,EAAE,UAAU;AAChD,oBAAc,GAAG;AACjB;AAAA,IACF;AAEA,WAAO,YAAY;AACnB,UAAM,SAAS;AAEf,UAAM;AAAA,MACJ,MAAM,gBAAgB;AAAA,MACtB,oBAAoB;AAAA,MACpB,iBAAiB;AAAA,MACjB,oBAAoB,CAAC;AAAA,MACrB,aAAa;AAAA,MACb,aAAa;AAAA,MACb,iBAAiB,CAAC;AAAA,MAClB,YAAY,CAAC;AAAA,MACb,QAAQ;AAAA,IACV,IAAI;AAEJ,UAAM,mBAAmB,qBAAqB;AAC9C,UAAM,uBAAuB,qBAAqB,mBAAmB,CAAC;AACtE,UAAM,aAAa,CAAC,qBAAqB,CAAC;AAG1C,QAAI,cAAc,OAAO;AACvB,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB,OAAO;AAAA,QACP,MAAM;AAAA,qJACuI,UAAU;AAAA,cACjJ,OAAO,UAAU,WAAW,MAAM,QAAQ,OAAO,MAAM,IAAI,EAAE;AAAA;AAAA,QAEnE,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,QACpB,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,QAClB,mBAAmB;AAAA,QACnB,mBAAmB;AAAA,QACnB,gBAAgB;AAAA,QAChB,gBAAgB;AAAA,MAClB,CAAC;AACD,UAAI,CAAC,OAAO,aAAa;AACvB,qBAAa,OAAO;AACpB;AAAA,MACF;AAAA,IACF;AAGA,UAAM,iBAAiB,OAAO,QAAQ,iBAAiB,EACpD,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,kBAAkB,EAAE,iBAAY,CAAC,WAAW,EAC7D,KAAK,EAAE;AACV,UAAM,cAAc,OAAO,QAAQ,cAAc,EAC9C,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,kBAAkB,EAAE,WAAW,CAAC,WAAW,EAC5D,KAAK,EAAE;AACV,UAAM,kBAAkB,UACrB,IAAI,OAAK,kBAAkB,EAAE,SAAS,KAAK,EAAE,SAAS,iBAAO,EAAE,KAAK,WAAW,EAC/E,KAAK,EAAE;AAEV,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY;AAClB,UAAM,YAAY,KAAK,eAAe;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,YAAY,KAAK;AAGxB,UAAM,IAAI,CAAkC,OAC1C,OAAQ,eAAe,EAAE;AAE3B,UAAM,aAAkB,EAAqB,gBAAgB;AAC7D,UAAM,aAAkB,EAAE,iBAAiB;AAC3C,UAAM,UAAkB,EAAE,cAAc;AACxC,UAAM,YAAkB,EAAE,eAAe;AACzC,UAAM,WAAkB,EAAE,SAAS;AACnC,UAAM,YAAkB,EAAqB,WAAW;AACxD,UAAM,cAAkB,EAAE,cAAc;AACxC,UAAM,cAAkB,EAAE,wBAAwB;AAClD,UAAM,cAAkB,EAAE,4BAA4B;AACtD,UAAM,kBAAkB,EAAE,qBAAqB;AAC/C,UAAM,iBAAkB,EAAE,iBAAiB;AAC3C,UAAM,cAAkB,EAAE,cAAc;AACxC,UAAM,kBAAkB,EAAE,mBAAmB;AAC7C,UAAM,eAAkB,EAAE,gBAAgB;AAE1C,QAAI,qBAAqB;AAEzB,UAAM,YAAY,MAAM;AACtB,YAAM,QAAQ,oBAAoB;AAClC,YAAM,OAAO,WAAW;AAExB,UAAI,OAAO;AACT,kBAAU,YAAY;AACtB,iBAAS,YAAa;AACtB,oBAAY,UAAU,OAAO,QAAQ;AACrC,uBAAe,MAAM,sBAAsB;AAC3C,wBAAgB,UAAU,OAAO,QAAQ;AACzC,oBAAY,UAAU,IAAI,QAAQ;AAClC,oBAAY,UAAU,OAAO,QAAQ;AACrC,mBAAW,UAAU,OAAO,UAAU,SAAS,MAAM;AACrD,gBAAQ,UAAU,OAAO,UAAa,SAAS,SAAS;AACxD,wBAAgB,UAAU,OAAO,UAAU,SAAS,MAAM;AAC1D,qBAAa,UAAU,OAAO,UAAa,SAAS,SAAS;AAC7D,oBAAY,MAAM,sBAAsB,SAAS,SAAS,YAAY;AAAA,MACxE,WAAW,qBAAqB;AAC9B,kBAAU,YAAY;AACtB,iBAAS,YAAa;AACtB,oBAAY,UAAU,IAAI,QAAQ;AAClC,uBAAe,MAAM,sBAAsB;AAC3C,wBAAgB,UAAU,IAAI,QAAQ;AACtC,oBAAY,UAAU,OAAO,QAAQ;AACrC,oBAAY,UAAU,IAAI,QAAQ;AAClC,mBAAW,UAAU,OAAO,UAAU,iBAAiB;AACvD,gBAAQ,UAAU,OAAO,UAAa,cAAc;AAAA,MACtD,OAAO;AACL,kBAAU,YAAY;AACtB,iBAAS,YAAa;AACtB,oBAAY,UAAU,IAAI,QAAQ;AAClC,uBAAe,MAAM,sBAAsB;AAC3C,wBAAgB,UAAU,OAAO,QAAQ;AACzC,oBAAY,UAAU,IAAI,QAAQ;AAClC,oBAAY,UAAU,IAAI,QAAQ;AAClC,mBAAW,UAAU,OAAO,UAAU,SAAS,MAAM;AACrD,gBAAQ,UAAU,OAAO,UAAa,SAAS,SAAS;AAAA,MAC1D;AAAA,IACF;AAEA,cAAU;AAGV,UAAM,UAAU,MAAM;AAAE,aAAQ,YAAY;AAAA,IAAI;AAEhD,eAAW,iBAAiB,UAAU,SAAS;AAC/C,MAAE,UAAU,EAAG,iBAAiB,SAAS,MAAM,aAAa,SAAS,OAAO,CAAC;AAE7E,MAAE,kBAAkB,EAAG,iBAAiB,SAAS,CAAC,MAAM;AACtD,QAAE,eAAe;AACjB,2BAAqB;AACrB,MAAC,EAAoB,SAAS,EAAI,QAAQ;AAC1C,gBAAU;AAAA,IACZ,CAAC;AAED,MAAoB,SAAS,EAAG,iBAAiB,SAAS,OAAK;AAC7D,YAAM,KAAK,EAAE;AACb,SAAG,QAAQ,GAAG,MAAM,QAAQ,gBAAgB,EAAE;AAAA,IAChD,CAAC;AAED,KAAC,mBAAmB,qBAAqB,kBAAkB,aAAa,EAAE,QAAQ,QAAM;AA3N5F,UAAAC;AA4NM,OAAAA,MAAA,EAAoB,EAAE,MAAtB,gBAAAA,IAAyB,iBAAiB,SAAS,OAAK;AACtD,cAAM,KAAK,EAAE;AACb,WAAG,QAAQ,GAAG,MAAM,QAAQ,OAAO,EAAE;AAAA,MACvC;AAAA,IACF,CAAC;AAED,WAAO,iBAAuD,mCAAmC,EAC9F,QAAQ,QAAM;AACb,YAAM,OAAO,MAAM,GAAG,QAAQ,WAAW,EAAG,UAAU,OAAO,WAAW,GAAG,UAAU,EAAE;AACvF,SAAG,iBAAiB,SAAS,IAAI;AACjC,SAAG,iBAAiB,UAAU,IAAI;AAClC,WAAK;AAAA,IACP,CAAC;AAGH,cAAU,iBAAiB,SAAS,YAAY;AA3OpD,UAAAA,KAAA;AA4OM,YAAM,SAAS,CAAC,OAAY;AA5OlC,YAAAA,KAAAC;AA6OS,gBAAAA,OAAAD,MAAA,EAAoB,EAAE,MAAtB,gBAAAA,IAA0B,MAAM,WAAhC,OAAAC,MAA0C;AAAA;AAE7C,YAAM,QAAc,oBAAoB;AACxC,YAAM,cAAc,QAAQ,OAAO,iBAAiB,IAAI;AAExD,UAAI,CAAC,WAAW,UAAU,SAAS,QAAQ,GAAG;AAC5C,YAAI,KAAC,4BAAc,OAAO,mBAAmB,GAAG,YAAY,UAAU,GAAG;AACvE,eAAK,EAAE,MAAM,WAAW,OAAO,kBAAkB,MAAM,eAAU,UAAU,iBAAO,UAAU,KAAK,oBAAoB,OAAO,CAAC;AAC7H;AAAA,QACF;AAAA,MACF;AACA,UAAI,SAAS,CAAC,aAAa;AACzB,aAAK,EAAE,MAAM,WAAW,OAAO,SAAS,MAAM,qCAAqC,oBAAoB,OAAO,CAAC;AAC/G;AAAA,MACF;AAEA,gBAAU,WAAa;AACvB,gBAAU,YAAa;AAEvB,YAAM,SAAS,EAAE,SAAS,OAAO,QAAQ,aAAa,MAAM,OAAO,SAAS,EAAE;AAE9E,UAAI;AACF,YAAI,CAAC,WAAW,UAAU,SAAS,QAAQ,GAAG;AAC5C,gBAAM,KAAK,OAAO,iBAAiB,gDAC9B,SAD8B;AAAA,YAEjC,oBAAoB,OAAO,oBAAoB;AAAA,YAC/C,mBAAoB,OAAO,mBAAmB;AAAA,cAC1C,SAAS,EAAE,gBAAgB,QAAQ,gBAAgB,OAAO,gBAAgB,EAAE,EACjF;AAAA,QACH;AACA,YAAI,CAAC,QAAQ,UAAU,SAAS,QAAQ,GAAG;AACzC,gBAAM,KAAK,OAAO,cAAc,gDAC3B,SAD2B;AAAA,YAE9B,iBAAiB,OAAO,iBAAiB;AAAA,YACzC,gBAAiB,OAAO,gBAAgB;AAAA,cACpC,SAAS,EAAE,gBAAgB,QAAQ,gBAAgB,OAAO,aAAa,EAAE,EAC9E;AAAA,QACH;AAEA,wBAAgB,YAAY;AAC5B,eAAAD,MAAA,KAAK,MAAK,cAAV,wBAAAA,KAAsB,EAAE,SAAS,KAAK;AAItC,aAAK;AAAA,UACH,MAAM;AAAA,UACN,OAAO;AAAA,UACP,MAAM;AAAA,UACN,oBAAoB;AAAA,UACpB,UAAU,MAAM,aAAa,SAAS,SAAS,IAAI;AAAA,QACrD,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,wBAAgB,QAAQ;AACxB,aAAK,EAAE,MAAM,SAAS,OAAO,SAAS,MAAO,IAAc,SAAS,oBAAoB,OAAO,CAAC;AAChG,kBAAU,WAAY;AACtB,kBAAU,YAAY;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,cAAc,QAAoB,QAA8C;AA3S1F;AA4SI,UAAM,KAAK,SAAS,cAAc,KAAK;AACvC,OAAG,YAAY;AAAA;AAAA;AAAA;AAAA,WAIT,uBAAY,MAAM,EAAE,MAAM,sDAAsD,MAAhF,mBAAoF,OAApF,YAA0F,EAAE;AAAA;AAAA;AAGlG,WAAO,YAAY,EAAE;AAAA,EACvB;AAAA,EAEQ,eAAe,GASZ;AACT,WAAO;AAAA,eACI,YAAY,EAAE,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAkBE,0BAAY,EAAE,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uEAUY,EAAE,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gDA4BtC,EAAE,kBAAkB,yCAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAMhF,EAAE,UAAU,iBAAO,EAAE,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6CASV,EAAE,eAAe,yCAAoC;AAAA;AAAA;AAAA;AAAA,4CAItD,EAAE,mBAAmB,yCAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCnG;AACF;;;ADpaO,IAAM,kBAAkB;AAAA,EAC7B,OAAO,QAAsB;AAf/B;AAgBI,UAAM,SAAS,IAAI,YAAY,iCAC1B,SAD0B;AAAA,MAE7B,OAAO,iCACF,OAAO,QADL;AAAA,QAEL,YAAQ,6BAAc,YAAO,UAAP,mBAAc,MAAM;AAAA,MAC5C;AAAA,IACF,EAAC;AACD,WAAO;AAAA,MACL,OAAO,MAAM,OAAO,MAAM;AAAA,IAC5B;AAAA,EACF;AACF;","names":["import_core","import_core","_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/widget.ts","../src/dialog.ts","../src/webview.ts","../src/styles.ts"],"sourcesContent":["import { resolveAccent } from '@auto-topup/core';\nimport { TopupWidget } from './widget.js';\nimport type { TopupCallbacks } from '@auto-topup/core';\n\nexport type { TopupCallbacks };\nexport { TopupWidget };\n\nexport interface CreateConfig extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\ntheme?: { accent?: string; fontFamily?: string };\n}\n\nexport const RetailcodeTopup = {\n create(config: CreateConfig) {\n const widget = new TopupWidget({\n ...config,\n theme: {\n ...config.theme,\n accent: resolveAccent(config.theme?.accent),\n },\n });\n return {\n mount: () => widget.mount(),\n };\n },\n};\n","import {\n RetailcodeApiClient,\n RetailcodeApiError,\n TopupApiConfig,\n TopupCallbacks,\n resolveAccent,\n formatPhone,\n isValidMsisdn,\n isValidAmount,\n} from '@auto-topup/core';\nimport { loadSwal, swal } from './dialog.js';\nimport { closeWebview, updateUrlStatus } from './webview.js';\nimport { buildTokens, buildStyles } from './styles.js';\n\nfunction showTermsDialog(opts: {\n html: string;\n accent: string;\n fontFamily: string;\n}): Promise<boolean> {\n return new Promise((resolve) => {\n const overlay = document.createElement('div');\n overlay.style.cssText =\n 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;' +\n 'align-items:center;justify-content:center;z-index:99999;padding:16px;box-sizing:border-box;';\n\n const card = document.createElement('div');\n card.style.cssText =\n `background:#fff;border-radius:12px;width:100%;max-width:480px;` +\n `max-height:85vh;display:flex;flex-direction:column;font-family:${opts.fontFamily};`;\n\n const title = document.createElement('div');\n title.style.cssText =\n 'padding:20px 24px 12px;font-size:18px;font-weight:700;color:#111827;text-align:center;flex-shrink:0;';\n title.textContent = 'Terms & Conditions';\n\n const content = document.createElement('div');\n content.style.cssText =\n 'flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;' +\n 'touch-action:pan-y;overscroll-behavior:contain;' +\n 'padding:0 24px 16px;font-size:13px;line-height:1.65;color:#374151;';\n content.innerHTML = opts.html;\n\n const actions = document.createElement('div');\n actions.style.cssText =\n 'display:flex;gap:12px;padding:16px 24px;flex-shrink:0;border-top:1px solid #F3F4F6;';\n\n const dismiss = (agreed: boolean) => {\n document.body.style.overflow = '';\n document.body.removeChild(overlay);\n resolve(agreed);\n };\n\n const decline = document.createElement('button');\n decline.style.cssText =\n 'flex:1;padding:12px;border-radius:8px;border:none;background:#9CA3AF;' +\n 'color:#fff;font-size:14px;font-weight:600;cursor:pointer;';\n decline.textContent = 'Decline';\n decline.onclick = () => dismiss(false);\n\n const agree = document.createElement('button');\n agree.style.cssText =\n `flex:2;padding:12px;border-radius:8px;border:none;background:${opts.accent};` +\n 'color:#fff;font-size:14px;font-weight:600;cursor:pointer;';\n agree.textContent = 'I Agree & Continue';\n agree.onclick = () => dismiss(true);\n\n actions.appendChild(decline);\n actions.appendChild(agree);\n card.appendChild(title);\n card.appendChild(content);\n card.appendChild(actions);\n overlay.appendChild(card);\n document.body.style.overflow = 'hidden';\n document.body.appendChild(overlay);\n });\n}\n\ninterface WidgetOptions extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\n theme?: { accent?: string; fontFamily?: string };\n}\n\nexport class TopupWidget {\n private readonly client: RetailcodeApiClient;\n private readonly opts: WidgetOptions;\n\n constructor(opts: WidgetOptions) {\n this.opts = opts;\n this.client = new RetailcodeApiClient(opts.publicKey);\n }\n\n async mount(): Promise<void> {\n const { msisdn, container, onClose, theme = {} } = this.opts;\n\n const target = document.querySelector(container);\n if (!target) return;\n\n const accent = resolveAccent(theme.accent);\n const fontFamily = theme.fontFamily ?? \"'DM Sans', system-ui, sans-serif\";\n const tokens = buildTokens(accent, fontFamily);\n\n let shadow: ShadowRoot | null = null;\n\n const showInitError = (message: string) => {\n const html = `\n <div style=\"font-family:${fontFamily};padding:24px;border:1.5px solid #FCA5A5;background:#FEF2F2;border-radius:8px;color:#991B1B;text-align:center;max-width:440px;margin:40px auto;\">\n <div style=\"font-weight:700;font-size:15px;margin-bottom:4px;\">SDK failed to initialize</div>\n <div style=\"font-size:13px;opacity:0.8;\">${message}</div>\n </div>`;\n if (shadow) shadow.innerHTML = html;\n else (target as HTMLElement).innerHTML = html;\n };\n\n if (!isValidMsisdn(msisdn)) {\n showInitError('Subscriber phone number is not valid.');\n return;\n }\n\n shadow = target.attachShadow({ mode: 'open' });\n this.renderSpinner(shadow, tokens);\n\n let cfg: TopupApiConfig;\n try {\n cfg = await this.client.fetchConfig(msisdn);\n } catch (e) {\n const msg =\n e instanceof RetailcodeApiError ? e.message : 'Could not connect to the activation server.';\n showInitError(msg);\n return;\n }\n\n shadow.innerHTML = '';\n\n const {\n name: prefilledName = '',\n subscribedAirtime = false,\n subscribedData = false,\n airtimethresholds = {},\n airtimeMin = 0,\n airtimeMax = 10000,\n dataThresholds = {},\n dataPlans = [],\n terms = null,\n } = cfg;\n\n const isFullSubscriber = subscribedAirtime && subscribedData;\n const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;\n const isBrandNew = !subscribedAirtime && !subscribedData;\n\n // Terms gate for brand-new users — shown before loading SweetAlert2 CDN\n if (isBrandNew && terms) {\n const agreed = await showTermsDialog({\n html: typeof terms === 'string' ? terms.replace(/\\n/g, '<br>') : '',\n accent,\n fontFamily,\n });\n if (!agreed) {\n closeWebview(onClose);\n return;\n }\n }\n\n // Load SweetAlert2 for form validation/success toasts\n await loadSwal();\n\n // Build option HTML strings\n const airtimeOptions = Object.entries(airtimethresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ₦${k}</option>`)\n .join('');\n const dataOptions = Object.entries(dataThresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ${k}</option>`)\n .join('');\n const dataPlanOptions = dataPlans\n .map(p => `<option value=\"${p.productId}\">${p.allowance} — ₦${p.price}</option>`)\n .join('');\n\n const modal = document.createElement('div');\n modal.className = 'rc-modal';\n modal.innerHTML = this.buildModalHtml({\n tokens,\n msisdn,\n prefilledName,\n airtimeMin,\n airtimeMax,\n airtimeOptions,\n dataOptions,\n dataPlanOptions,\n });\n\n shadow.appendChild(modal);\n\n // ── Refs ────────────────────────────────────────────────────────────────\n const $ = <T extends Element = HTMLElement>(id: string) =>\n shadow!.getElementById(id) as T | null;\n\n const typeSelect = $<HTMLSelectElement>('rc-type-select')!;\n const airtimeSec = $('section-airtime')!;\n const dataSec = $('section-data')!;\n const titleText = $('rc-main-title')!;\n const descText = $('rc-desc')!;\n const submitBtn = $<HTMLButtonElement>('rc-submit')!;\n const msisdnField = $('field-msisdn')!;\n const depLinkWrap = $('rc-dependent-link-wrap')!;\n const depControls = $('section-dependent-controls')!;\n const typeSelectorGrp = $('group-type-selector')!;\n const beneficiaryRow = $('row-beneficiary')!;\n const spendingRow = $('row-spending')!;\n const airtimeMaxField = $('field-airtime-max')!;\n const dataMaxField = $('field-data-max')!;\n\n let forceDependentView = false;\n\n const refreshUI = () => {\n const isDep = isFullSubscriber || forceDependentView;\n const mode = typeSelect.value;\n\n if (isDep) {\n titleText.innerText = 'Add Dependent';\n descText.innerText = 'Configure subscription for someone else';\n msisdnField.classList.remove('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr 1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.remove('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n airtimeMaxField.classList.toggle('hidden', mode === 'data');\n dataMaxField.classList.toggle('hidden', mode === 'airtime');\n spendingRow.style.gridTemplateColumns = mode === 'both' ? '1fr 1fr' : '1fr';\n } else if (isPartialSubscriber) {\n titleText.innerText = 'Complete Profile';\n descText.innerText = 'Finish your Auto Topup subscription';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.add('hidden');\n depLinkWrap.classList.remove('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', subscribedAirtime);\n dataSec.classList.toggle('hidden', subscribedData);\n } else {\n titleText.innerText = 'Auto Topup Subscription';\n descText.innerText = 'Automate your airtime & data recharge';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n }\n };\n\n refreshUI();\n\n // ── Event wiring ────────────────────────────────────────────────────────\n const unmount = () => { shadow!.innerHTML = ''; };\n\n typeSelect.addEventListener('change', refreshUI);\n $('rc-close')!.addEventListener('click', () => closeWebview(onClose, unmount));\n\n $('rc-switch-to-dep')!.addEventListener('click', (e) => {\n e.preventDefault();\n forceDependentView = true;\n ($<HTMLInputElement>('rc-name'))!.value = '';\n refreshUI();\n });\n\n $<HTMLInputElement>('rc-name')!.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/[^a-zA-Z\\s]/g, '');\n });\n\n ['rc-msisdn-input', 'airtimeTopupValue', 'rc-airtime-max', 'rc-data-max'].forEach(id => {\n $<HTMLInputElement>(id)?.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/\\D/g, '');\n });\n });\n\n shadow.querySelectorAll<HTMLInputElement | HTMLSelectElement>('.rc-field input, .rc-field select')\n .forEach(el => {\n const sync = () => el.closest('.rc-field')!.classList.toggle('has-val', el.value !== '');\n el.addEventListener('input', sync);\n el.addEventListener('change', sync);\n sync();\n });\n\n // ── Submit ──────────────────────────────────────────────────────────────\n submitBtn.addEventListener('click', async () => {\n const getVal = (id: string) =>\n ($<HTMLInputElement>(id))?.value.trim() ?? '';\n\n const isDep = isFullSubscriber || forceDependentView;\n const beneficiary = isDep ? getVal('rc-msisdn-input') : msisdn;\n\n if (!airtimeSec.classList.contains('hidden')) {\n if (!isValidAmount(getVal('airtimeTopupValue'), airtimeMin, airtimeMax)) {\n swal({ icon: 'warning', title: 'Invalid Amount', text: `Enter ₦${airtimeMin} – ₦${airtimeMax}.`, confirmButtonColor: accent });\n return;\n }\n }\n if (isDep && !beneficiary) {\n swal({ icon: 'warning', title: 'Wait!', text: 'Enter the dependent phone number.', confirmButtonColor: accent });\n return;\n }\n\n submitBtn.disabled = true;\n submitBtn.innerText = 'Processing…';\n\n const common = { network: 'MTN', msisdn: beneficiary, name: getVal('rc-name') };\n\n try {\n if (!airtimeSec.classList.contains('hidden')) {\n await this.client.subscribeAirtime({\n ...common,\n airtimeThresholdId: getVal('airtimeThresholdId'),\n airtimeTopupValue: getVal('airtimeTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-airtime-max') }),\n });\n }\n if (!dataSec.classList.contains('hidden')) {\n await this.client.subscribeData({\n ...common,\n dataThresholdId: getVal('dataThresholdId'),\n dataTopupValue: getVal('dataTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-data-max') }),\n });\n }\n\n updateUrlStatus('successful');\n this.opts.onSuccess?.({ success: true });\n // Use didClose so we notify the native host AFTER SweetAlert2 finishes\n // its close animation — prevents the black-screen flash that occurs when\n // Flutter/iOS dismisses the sheet while the backdrop is still fading out.\n swal({\n icon: 'success',\n title: 'Subscription Active!',\n text: 'Your auto top-up is now enabled.',\n confirmButtonColor: accent,\n didClose: () => closeWebview(onClose, unmount, true),\n });\n } catch (err) {\n updateUrlStatus('failed');\n swal({ icon: 'error', title: 'Oops!', text: (err as Error).message, confirmButtonColor: accent });\n submitBtn.disabled = false;\n submitBtn.innerText = 'Activate Subscription';\n }\n });\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private renderSpinner(shadow: ShadowRoot, tokens: ReturnType<typeof buildTokens>): void {\n const el = document.createElement('div');\n el.innerHTML = `\n <style>\n :host { display:flex; align-items:center; justify-content:center; min-height:100vh; background:#fff; }\n @media (min-width:560px) { :host { background:rgba(0,0,0,.50); } }\n ${buildStyles(tokens).match(/\\.rc-spinner-ring[\\s\\S]*?@keyframes rc-spin[\\s\\S]*?}/)?.[0] ?? ''}\n </style>\n <div class=\"rc-spinner-ring\"></div>`;\n shadow.appendChild(el);\n }\n\n private buildModalHtml(p: {\n tokens: ReturnType<typeof buildTokens>;\n msisdn: string;\n prefilledName: string;\n airtimeMin: number;\n airtimeMax: number;\n airtimeOptions: string;\n dataOptions: string;\n dataPlanOptions: string;\n }): string {\n return `\n <style>${buildStyles(p.tokens)}</style>\n\n <div class=\"rc-header\">\n <div class=\"rc-header-top\">\n <div class=\"rc-title\">\n <h2 id=\"rc-main-title\">Auto Topup Subscription</h2>\n <p id=\"rc-desc\">Automate your airtime & data recharge</p>\n </div>\n <button class=\"rc-close-btn\" id=\"rc-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"rc-account-bar\">\n <div class=\"rc-dot\"></div>\n <div>\n <span class=\"rc-acc-label\">Your Phone Number</span>\n <span class=\"rc-acc-no\">${formatPhone(p.msisdn)}</span>\n </div>\n </div>\n </div>\n\n <div class=\"rc-content\">\n <div class=\"rc-group\">\n <p class=\"rc-group-title\">Registration Details</p>\n <div class=\"rc-row\" id=\"row-beneficiary\">\n <div class=\"rc-field\" id=\"field-name\">\n <input type=\"text\" id=\"rc-name\" placeholder=\" \" value=\"${p.prefilledName}\" autocomplete=\"name\">\n <label>Full Name</label>\n </div>\n <div class=\"rc-field hidden\" id=\"field-msisdn\">\n <input type=\"tel\" id=\"rc-msisdn-input\" placeholder=\" \" autocomplete=\"tel\">\n <label>Phone Number</label>\n </div>\n </div>\n </div>\n\n <div class=\"rc-group\" id=\"group-type-selector\">\n <p class=\"rc-group-title\">Subscription Type</p>\n <div class=\"rc-field has-val\">\n <select id=\"rc-type-select\">\n <option value=\"airtime\">Airtime Only</option>\n <option value=\"data\">Data Only</option>\n <option value=\"both\">Both Airtime & Data</option>\n </select>\n <label>Automation Mode</label>\n </div>\n </div>\n\n <hr class=\"rc-divider\">\n\n <div id=\"section-airtime\" class=\"section-animate\">\n <p class=\"rc-group-title\">Airtime Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"airtimeThresholdId\">${p.airtimeOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field\">\n <input type=\"tel\" id=\"airtimeTopupValue\" placeholder=\" \">\n <label>Amount (₦)</label>\n <small>₦${p.airtimeMin} – ₦${p.airtimeMax}</small>\n </div>\n </div>\n </div>\n\n <div id=\"section-data\" class=\"hidden section-animate\">\n <p class=\"rc-group-title\">Data Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"dataThresholdId\">${p.dataOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field has-val\">\n <select id=\"dataTopupValue\">${p.dataPlanOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Select Plan</label>\n </div>\n </div>\n </div>\n\n <div id=\"section-dependent-controls\" class=\"hidden\">\n <hr class=\"rc-divider\">\n <p class=\"rc-group-title\">Spending Controls</p>\n <div class=\"rc-row\" id=\"row-spending\">\n <div class=\"rc-field\" id=\"field-airtime-max\">\n <input type=\"tel\" id=\"rc-airtime-max\" placeholder=\" \">\n <label>Airtime Limit (₦)</label>\n </div>\n <div class=\"rc-field\" id=\"field-data-max\">\n <input type=\"tel\" id=\"rc-data-max\" placeholder=\" \">\n <label>Data Limit (₦)</label>\n </div>\n </div>\n </div>\n\n <button class=\"rc-submit-btn\" id=\"rc-submit\">Activate Subscription</button>\n\n <div id=\"rc-dependent-link-wrap\" class=\"rc-link-wrap hidden\">\n Or <a id=\"rc-switch-to-dep\">add a dependent instead</a>\n </div>\n </div>\n\n <div class=\"rc-footer\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"/>\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>\n </svg>\n Secured by <strong> Retailcode</strong>\n </div>\n `;\n }\n}\n","// Lazy-loads SweetAlert2 from CDN on first call.\n// The Swal reference is typed loosely so we don't need @types/sweetalert2.\n\ndeclare global {\n interface Window {\n Swal: SwalStatic;\n }\n}\n\ninterface SwalStatic {\n fire(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }>;\n}\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function loadSwal(): Promise<void> {\n if (window.Swal) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css';\n document.head.appendChild(link);\n\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11';\n script.onload = () => resolve();\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\nexport function swal(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }> {\n return window.Swal.fire(opts);\n}\n","// Bridges for closing the widget across all host environments.\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: { postMessage(msg: string): void };\n webkit?: { messageHandlers?: { retailcode?: { postMessage(msg: unknown): void } } };\n Android?: { close?(): void };\n RetailcodeFlutter?: { postMessage(msg: string): void }; // Flutter JavascriptChannel\n }\n}\n\nexport function updateUrlStatus(status: 'successful' | 'failed'): void {\n const url = new URL(window.location.href);\n url.searchParams.set('status', status);\n window.history.replaceState({}, '', url);\n}\n\nexport function closeWebview(\n onClose?: (r: { closed: true }) => void,\n unmount?: () => void,\n success = false,\n): void {\n const url = new URL(window.location.href);\n url.searchParams.set('isClose', 'true');\n window.history.replaceState({}, '', url);\n\n const isNative = !!(\n window.ReactNativeWebView ||\n window.RetailcodeFlutter ||\n window.webkit?.messageHandlers?.retailcode ||\n window.Android?.close\n );\n\n // In native WebView contexts the host app dismisses the whole view, so\n // clearing the DOM first causes a black-screen flash. Only unmount for\n // plain browser usage where there is no native dismiss.\n if (!isNative) unmount?.();\n\n onClose?.({ closed: true });\n\n // Include success flag so native apps know whether to fire onSuccess\n // before dismissing — this way the dialog is always fully visible first.\n const msg = JSON.stringify({ action: 'close', success });\n\n if (window.ReactNativeWebView) {\n window.ReactNativeWebView.postMessage(msg);\n } else if (window.RetailcodeFlutter) {\n window.RetailcodeFlutter.postMessage(msg);\n } else if (window.webkit?.messageHandlers?.retailcode) {\n window.webkit.messageHandlers.retailcode.postMessage({ action: 'close', success });\n } else if (window.Android?.close) {\n window.Android.close();\n }\n}\n","import { mix, rgba } from '@auto-topup/core';\n\nexport interface StyleTokens {\n accent: string;\n accentHover: string;\n accentFocus: string;\n accentShadow: string;\n fontFamily: string;\n}\n\nexport function buildTokens(accent: string, fontFamily: string): StyleTokens {\n return {\n accent,\n accentHover: mix(accent, -10),\n accentFocus: rgba(accent, 0.15),\n accentShadow: rgba(accent, 0.25),\n fontFamily,\n };\n}\n\nexport function buildStyles(t: StyleTokens): string {\n return `\n @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :host {\n display: flex;\n justify-content: center;\n min-height: 100vh;\n font-family: ${t.fontFamily};\n background: #FFFFFF;\n }\n @media (min-width: 560px) {\n :host { background: rgba(0,0,0,.50); align-items: center; padding: 20px; }\n }\n\n .rc-modal {\n width: 100%; max-width: 440px; background: #FFFFFF; overflow: hidden;\n animation: modalSlide .35s cubic-bezier(.22,1,.36,1) both;\n border-radius: 0; box-shadow: none;\n }\n @media (min-width: 560px) {\n .rc-modal {\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,.06), 0 12px 32px rgba(0,0,0,.12), 0 32px 64px rgba(0,0,0,.08);\n }\n }\n @keyframes modalSlide { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:none; } }\n\n .rc-header { padding: 24px 20px 14px; }\n .rc-header-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }\n .rc-title h2 { font-size: 19px; font-weight: 700; color: #111827; letter-spacing: -.3px; }\n .rc-title p { font-size: 13px; color: #6B7280; margin-top: 3px; line-height: 1.4; }\n\n .rc-close-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; background: #F3F4F6; border: none;\n border-radius: 6px; color: #6B7280; cursor: pointer; transition: background .15s; flex-shrink: 0;\n }\n .rc-close-btn:hover { background: #E5E7EB; }\n\n .rc-account-bar {\n display: flex; align-items: center; background: ${t.accent};\n padding: 10px 14px; border-radius: 6px; color: #fff;\n }\n .rc-dot { width: 8px; height: 8px; background: #4ADE80; border-radius: 50%; margin-right: 10px; box-shadow: 0 0 0 2px rgba(74,222,128,.3); flex-shrink: 0; }\n .rc-acc-label { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .65; display: block; margin-bottom: 1px; }\n .rc-acc-no { font-size: 14px; font-weight: 700; letter-spacing: .03em; }\n\n .rc-content { padding: 20px 20px 16px; }\n .rc-group { margin-bottom: 14px; }\n .rc-group-title { font-size: 10px; font-weight: 700; color: #9CA3AF; text-transform: uppercase; letter-spacing: .09em; margin-bottom: 10px; }\n\n .rc-field { position: relative; margin-bottom: 10px; }\n .rc-field:last-child { margin-bottom: 0; }\n .rc-field label { position: absolute; left: 12px; top: 16px; font-size: 13.5px; color: #9CA3AF; transition: all .16s cubic-bezier(.4,0,.2,1); pointer-events: none; transform-origin: left top; }\n .rc-field input, .rc-field select {\n width: 100%; height: 52px; padding: 20px 12px 6px;\n background: #F9FAFB; border: 1.5px solid #E5E7EB; border-radius: 6px;\n font-family: inherit; font-size: 14px; font-weight: 500; color: #111827;\n outline: none; transition: border-color .18s, box-shadow .18s, background .18s;\n appearance: none; -webkit-appearance: none;\n }\n .rc-field select {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px; cursor: pointer;\n }\n .rc-field input:focus, .rc-field select:focus {\n border-color: ${t.accent}; background: #FFFFFF; box-shadow: 0 0 0 3px ${t.accentFocus};\n }\n .rc-field.has-val label, .rc-field input:focus ~ label, .rc-field select:focus ~ label {\n top: 7px; font-size: 10.5px; font-weight: 700; color: ${t.accent};\n }\n .rc-field small { display: block; margin-top: 4px; font-size: 11px; color: #9CA3AF; }\n\n .rc-row { display: grid; gap: 10px; margin-bottom: 10px; }\n .rc-row .rc-field { margin-bottom: 0; }\n .rc-divider { height: 1px; background: #F3F4F6; margin: 16px 0; border: none; }\n .section-animate { animation: fadeIn .25s ease-out; }\n @keyframes fadeIn { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }\n\n .rc-link-wrap { margin-top: 12px; text-align: center; font-size: 13px; color: #9CA3AF; }\n .rc-link-wrap a { color: ${t.accent}; text-decoration: none; font-weight: 600; cursor: pointer; }\n .rc-link-wrap a:hover { text-decoration: underline; }\n\n .rc-submit-btn {\n width: 100%; height: 52px; background: ${t.accent}; color: #FFFFFF; border: none;\n border-radius: 6px; font-family: inherit; font-size: 15px; font-weight: 700;\n cursor: pointer; letter-spacing: .01em; margin-top: 6px;\n box-shadow: 0 4px 12px ${t.accentShadow};\n transition: background .18s, transform .1s, box-shadow .18s;\n }\n .rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${rgba(t.accent, 0.35)}; }\n .rc-submit-btn:active:not(:disabled) { transform: translateY(0); }\n .rc-submit-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }\n\n .rc-spinner-ring {\n width: 48px; height: 48px;\n border: 4px solid ${rgba(t.accent, 0.18)};\n border-top-color: ${t.accent};\n border-radius: 50%;\n animation: rc-spin .75s linear infinite;\n }\n @keyframes rc-spin { to { transform: rotate(360deg); } }\n\n .rc-footer {\n text-align: center; padding: 0 20px 18px; font-size: 11px; color: #D1D5DB;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n }\n .rc-footer svg { opacity: .5; }\n .hidden { display: none !important; }\n `;\n}\n"],"mappings":"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","names":["import_core","import_core","_a","_b"]}
|
package/dist/index.mjs
CHANGED
|
@@ -204,6 +204,43 @@ function buildStyles(t) {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
// src/widget.ts
|
|
207
|
+
function showTermsDialog(opts) {
|
|
208
|
+
return new Promise((resolve) => {
|
|
209
|
+
const overlay = document.createElement("div");
|
|
210
|
+
overlay.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:99999;padding:16px;box-sizing:border-box;";
|
|
211
|
+
const card = document.createElement("div");
|
|
212
|
+
card.style.cssText = `background:#fff;border-radius:12px;width:100%;max-width:480px;max-height:85vh;display:flex;flex-direction:column;font-family:${opts.fontFamily};`;
|
|
213
|
+
const title = document.createElement("div");
|
|
214
|
+
title.style.cssText = "padding:20px 24px 12px;font-size:18px;font-weight:700;color:#111827;text-align:center;flex-shrink:0;";
|
|
215
|
+
title.textContent = "Terms & Conditions";
|
|
216
|
+
const content = document.createElement("div");
|
|
217
|
+
content.style.cssText = "flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;overscroll-behavior:contain;padding:0 24px 16px;font-size:13px;line-height:1.65;color:#374151;";
|
|
218
|
+
content.innerHTML = opts.html;
|
|
219
|
+
const actions = document.createElement("div");
|
|
220
|
+
actions.style.cssText = "display:flex;gap:12px;padding:16px 24px;flex-shrink:0;border-top:1px solid #F3F4F6;";
|
|
221
|
+
const dismiss = (agreed) => {
|
|
222
|
+
document.body.style.overflow = "";
|
|
223
|
+
document.body.removeChild(overlay);
|
|
224
|
+
resolve(agreed);
|
|
225
|
+
};
|
|
226
|
+
const decline = document.createElement("button");
|
|
227
|
+
decline.style.cssText = "flex:1;padding:12px;border-radius:8px;border:none;background:#9CA3AF;color:#fff;font-size:14px;font-weight:600;cursor:pointer;";
|
|
228
|
+
decline.textContent = "Decline";
|
|
229
|
+
decline.onclick = () => dismiss(false);
|
|
230
|
+
const agree = document.createElement("button");
|
|
231
|
+
agree.style.cssText = `flex:2;padding:12px;border-radius:8px;border:none;background:${opts.accent};color:#fff;font-size:14px;font-weight:600;cursor:pointer;`;
|
|
232
|
+
agree.textContent = "I Agree & Continue";
|
|
233
|
+
agree.onclick = () => dismiss(true);
|
|
234
|
+
actions.appendChild(decline);
|
|
235
|
+
actions.appendChild(agree);
|
|
236
|
+
card.appendChild(title);
|
|
237
|
+
card.appendChild(content);
|
|
238
|
+
card.appendChild(actions);
|
|
239
|
+
overlay.appendChild(card);
|
|
240
|
+
document.body.style.overflow = "hidden";
|
|
241
|
+
document.body.appendChild(overlay);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
207
244
|
var TopupWidget = class {
|
|
208
245
|
constructor(opts) {
|
|
209
246
|
this.opts = opts;
|
|
@@ -242,7 +279,6 @@ var TopupWidget = class {
|
|
|
242
279
|
return;
|
|
243
280
|
}
|
|
244
281
|
shadow.innerHTML = "";
|
|
245
|
-
await loadSwal();
|
|
246
282
|
const {
|
|
247
283
|
name: prefilledName = "",
|
|
248
284
|
subscribedAirtime = false,
|
|
@@ -258,26 +294,17 @@ var TopupWidget = class {
|
|
|
258
294
|
const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;
|
|
259
295
|
const isBrandNew = !subscribedAirtime && !subscribedData;
|
|
260
296
|
if (isBrandNew && terms) {
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
${typeof terms === "string" ? terms.replace(/\n/g, "<br>") : ""}
|
|
266
|
-
</div>`,
|
|
267
|
-
confirmButtonText: "I Agree & Continue",
|
|
268
|
-
confirmButtonColor: accent,
|
|
269
|
-
showCancelButton: true,
|
|
270
|
-
cancelButtonText: "Decline",
|
|
271
|
-
cancelButtonColor: "#9CA3AF",
|
|
272
|
-
allowOutsideClick: false,
|
|
273
|
-
allowEscapeKey: false,
|
|
274
|
-
reverseButtons: true
|
|
297
|
+
const agreed = await showTermsDialog({
|
|
298
|
+
html: typeof terms === "string" ? terms.replace(/\n/g, "<br>") : "",
|
|
299
|
+
accent,
|
|
300
|
+
fontFamily
|
|
275
301
|
});
|
|
276
|
-
if (!
|
|
302
|
+
if (!agreed) {
|
|
277
303
|
closeWebview(onClose);
|
|
278
304
|
return;
|
|
279
305
|
}
|
|
280
306
|
}
|
|
307
|
+
await loadSwal();
|
|
281
308
|
const airtimeOptions = Object.entries(airtimethresholds).map(([k, id]) => `<option value="${id}">Below \u20A6${k}</option>`).join("");
|
|
282
309
|
const dataOptions = Object.entries(dataThresholds).map(([k, id]) => `<option value="${id}">Below ${k}</option>`).join("");
|
|
283
310
|
const dataPlanOptions = dataPlans.map((p) => `<option value="${p.productId}">${p.allowance} \u2014 \u20A6${p.price}</option>`).join("");
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/widget.ts","../src/dialog.ts","../src/webview.ts","../src/styles.ts"],"sourcesContent":["import { resolveAccent } from '@auto-topup/core';\nimport { TopupWidget } from './widget.js';\nimport type { TopupCallbacks } from '@auto-topup/core';\n\nexport type { TopupCallbacks };\nexport { TopupWidget };\n\nexport interface CreateConfig extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\ntheme?: { accent?: string; fontFamily?: string };\n}\n\nexport const RetailcodeTopup = {\n create(config: CreateConfig) {\n const widget = new TopupWidget({\n ...config,\n theme: {\n ...config.theme,\n accent: resolveAccent(config.theme?.accent),\n },\n });\n return {\n mount: () => widget.mount(),\n };\n },\n};\n","import {\n RetailcodeApiClient,\n RetailcodeApiError,\n TopupApiConfig,\n TopupCallbacks,\n resolveAccent,\n formatPhone,\n isValidMsisdn,\n isValidAmount,\n} from '@auto-topup/core';\nimport { loadSwal, swal } from './dialog.js';\nimport { closeWebview, updateUrlStatus } from './webview.js';\nimport { buildTokens, buildStyles } from './styles.js';\n\ninterface WidgetOptions extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\n theme?: { accent?: string; fontFamily?: string };\n}\n\nexport class TopupWidget {\n private readonly client: RetailcodeApiClient;\n private readonly opts: WidgetOptions;\n\n constructor(opts: WidgetOptions) {\n this.opts = opts;\n this.client = new RetailcodeApiClient(opts.publicKey);\n }\n\n async mount(): Promise<void> {\n const { msisdn, container, onClose, theme = {} } = this.opts;\n\n const target = document.querySelector(container);\n if (!target) return;\n\n const accent = resolveAccent(theme.accent);\n const fontFamily = theme.fontFamily ?? \"'DM Sans', system-ui, sans-serif\";\n const tokens = buildTokens(accent, fontFamily);\n\n let shadow: ShadowRoot | null = null;\n\n const showInitError = (message: string) => {\n const html = `\n <div style=\"font-family:${fontFamily};padding:24px;border:1.5px solid #FCA5A5;background:#FEF2F2;border-radius:8px;color:#991B1B;text-align:center;max-width:440px;margin:40px auto;\">\n <div style=\"font-weight:700;font-size:15px;margin-bottom:4px;\">SDK failed to initialize</div>\n <div style=\"font-size:13px;opacity:0.8;\">${message}</div>\n </div>`;\n if (shadow) shadow.innerHTML = html;\n else (target as HTMLElement).innerHTML = html;\n };\n\n if (!isValidMsisdn(msisdn)) {\n showInitError('Subscriber phone number is not valid.');\n return;\n }\n\n shadow = target.attachShadow({ mode: 'open' });\n this.renderSpinner(shadow, tokens);\n\n let cfg: TopupApiConfig;\n try {\n cfg = await this.client.fetchConfig(msisdn);\n } catch (e) {\n const msg =\n e instanceof RetailcodeApiError ? e.message : 'Could not connect to the activation server.';\n showInitError(msg);\n return;\n }\n\n shadow.innerHTML = '';\n await loadSwal();\n\n const {\n name: prefilledName = '',\n subscribedAirtime = false,\n subscribedData = false,\n airtimethresholds = {},\n airtimeMin = 0,\n airtimeMax = 10000,\n dataThresholds = {},\n dataPlans = [],\n terms = null,\n } = cfg;\n\n const isFullSubscriber = subscribedAirtime && subscribedData;\n const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;\n const isBrandNew = !subscribedAirtime && !subscribedData;\n\n // Terms gate for brand-new users\n if (isBrandNew && terms) {\n const result = await swal({\n title: 'Terms & Conditions',\n html: `\n <div style=\"text-align:left;font-size:13px;line-height:1.65;color:#374151;max-height:340px;overflow-y:auto;padding-right:4px;font-family:${fontFamily};\">\n ${typeof terms === 'string' ? terms.replace(/\\n/g, '<br>') : ''}\n </div>`,\n confirmButtonText: 'I Agree & Continue',\n confirmButtonColor: accent,\n showCancelButton: true,\n cancelButtonText: 'Decline',\n cancelButtonColor: '#9CA3AF',\n allowOutsideClick: false,\n allowEscapeKey: false,\n reverseButtons: true,\n });\n if (!result.isConfirmed) {\n closeWebview(onClose);\n return;\n }\n }\n\n // Build option HTML strings\n const airtimeOptions = Object.entries(airtimethresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ₦${k}</option>`)\n .join('');\n const dataOptions = Object.entries(dataThresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ${k}</option>`)\n .join('');\n const dataPlanOptions = dataPlans\n .map(p => `<option value=\"${p.productId}\">${p.allowance} — ₦${p.price}</option>`)\n .join('');\n\n const modal = document.createElement('div');\n modal.className = 'rc-modal';\n modal.innerHTML = this.buildModalHtml({\n tokens,\n msisdn,\n prefilledName,\n airtimeMin,\n airtimeMax,\n airtimeOptions,\n dataOptions,\n dataPlanOptions,\n });\n\n shadow.appendChild(modal);\n\n // ── Refs ────────────────────────────────────────────────────────────────\n const $ = <T extends Element = HTMLElement>(id: string) =>\n shadow!.getElementById(id) as T | null;\n\n const typeSelect = $<HTMLSelectElement>('rc-type-select')!;\n const airtimeSec = $('section-airtime')!;\n const dataSec = $('section-data')!;\n const titleText = $('rc-main-title')!;\n const descText = $('rc-desc')!;\n const submitBtn = $<HTMLButtonElement>('rc-submit')!;\n const msisdnField = $('field-msisdn')!;\n const depLinkWrap = $('rc-dependent-link-wrap')!;\n const depControls = $('section-dependent-controls')!;\n const typeSelectorGrp = $('group-type-selector')!;\n const beneficiaryRow = $('row-beneficiary')!;\n const spendingRow = $('row-spending')!;\n const airtimeMaxField = $('field-airtime-max')!;\n const dataMaxField = $('field-data-max')!;\n\n let forceDependentView = false;\n\n const refreshUI = () => {\n const isDep = isFullSubscriber || forceDependentView;\n const mode = typeSelect.value;\n\n if (isDep) {\n titleText.innerText = 'Add Dependent';\n descText.innerText = 'Configure subscription for someone else';\n msisdnField.classList.remove('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr 1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.remove('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n airtimeMaxField.classList.toggle('hidden', mode === 'data');\n dataMaxField.classList.toggle('hidden', mode === 'airtime');\n spendingRow.style.gridTemplateColumns = mode === 'both' ? '1fr 1fr' : '1fr';\n } else if (isPartialSubscriber) {\n titleText.innerText = 'Complete Profile';\n descText.innerText = 'Finish your Auto Topup subscription';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.add('hidden');\n depLinkWrap.classList.remove('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', subscribedAirtime);\n dataSec.classList.toggle('hidden', subscribedData);\n } else {\n titleText.innerText = 'Auto Topup Subscription';\n descText.innerText = 'Automate your airtime & data recharge';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n }\n };\n\n refreshUI();\n\n // ── Event wiring ────────────────────────────────────────────────────────\n const unmount = () => { shadow!.innerHTML = ''; };\n\n typeSelect.addEventListener('change', refreshUI);\n $('rc-close')!.addEventListener('click', () => closeWebview(onClose, unmount));\n\n $('rc-switch-to-dep')!.addEventListener('click', (e) => {\n e.preventDefault();\n forceDependentView = true;\n ($<HTMLInputElement>('rc-name'))!.value = '';\n refreshUI();\n });\n\n $<HTMLInputElement>('rc-name')!.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/[^a-zA-Z\\s]/g, '');\n });\n\n ['rc-msisdn-input', 'airtimeTopupValue', 'rc-airtime-max', 'rc-data-max'].forEach(id => {\n $<HTMLInputElement>(id)?.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/\\D/g, '');\n });\n });\n\n shadow.querySelectorAll<HTMLInputElement | HTMLSelectElement>('.rc-field input, .rc-field select')\n .forEach(el => {\n const sync = () => el.closest('.rc-field')!.classList.toggle('has-val', el.value !== '');\n el.addEventListener('input', sync);\n el.addEventListener('change', sync);\n sync();\n });\n\n // ── Submit ──────────────────────────────────────────────────────────────\n submitBtn.addEventListener('click', async () => {\n const getVal = (id: string) =>\n ($<HTMLInputElement>(id))?.value.trim() ?? '';\n\n const isDep = isFullSubscriber || forceDependentView;\n const beneficiary = isDep ? getVal('rc-msisdn-input') : msisdn;\n\n if (!airtimeSec.classList.contains('hidden')) {\n if (!isValidAmount(getVal('airtimeTopupValue'), airtimeMin, airtimeMax)) {\n swal({ icon: 'warning', title: 'Invalid Amount', text: `Enter ₦${airtimeMin} – ₦${airtimeMax}.`, confirmButtonColor: accent });\n return;\n }\n }\n if (isDep && !beneficiary) {\n swal({ icon: 'warning', title: 'Wait!', text: 'Enter the dependent phone number.', confirmButtonColor: accent });\n return;\n }\n\n submitBtn.disabled = true;\n submitBtn.innerText = 'Processing…';\n\n const common = { network: 'MTN', msisdn: beneficiary, name: getVal('rc-name') };\n\n try {\n if (!airtimeSec.classList.contains('hidden')) {\n await this.client.subscribeAirtime({\n ...common,\n airtimeThresholdId: getVal('airtimeThresholdId'),\n airtimeTopupValue: getVal('airtimeTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-airtime-max') }),\n });\n }\n if (!dataSec.classList.contains('hidden')) {\n await this.client.subscribeData({\n ...common,\n dataThresholdId: getVal('dataThresholdId'),\n dataTopupValue: getVal('dataTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-data-max') }),\n });\n }\n\n updateUrlStatus('successful');\n this.opts.onSuccess?.({ success: true });\n // Use didClose so we notify the native host AFTER SweetAlert2 finishes\n // its close animation — prevents the black-screen flash that occurs when\n // Flutter/iOS dismisses the sheet while the backdrop is still fading out.\n swal({\n icon: 'success',\n title: 'Subscription Active!',\n text: 'Your auto top-up is now enabled.',\n confirmButtonColor: accent,\n didClose: () => closeWebview(onClose, unmount, true),\n });\n } catch (err) {\n updateUrlStatus('failed');\n swal({ icon: 'error', title: 'Oops!', text: (err as Error).message, confirmButtonColor: accent });\n submitBtn.disabled = false;\n submitBtn.innerText = 'Activate Subscription';\n }\n });\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private renderSpinner(shadow: ShadowRoot, tokens: ReturnType<typeof buildTokens>): void {\n const el = document.createElement('div');\n el.innerHTML = `\n <style>\n :host { display:flex; align-items:center; justify-content:center; min-height:100vh; background:#fff; }\n @media (min-width:560px) { :host { background:rgba(0,0,0,.50); } }\n ${buildStyles(tokens).match(/\\.rc-spinner-ring[\\s\\S]*?@keyframes rc-spin[\\s\\S]*?}/)?.[0] ?? ''}\n </style>\n <div class=\"rc-spinner-ring\"></div>`;\n shadow.appendChild(el);\n }\n\n private buildModalHtml(p: {\n tokens: ReturnType<typeof buildTokens>;\n msisdn: string;\n prefilledName: string;\n airtimeMin: number;\n airtimeMax: number;\n airtimeOptions: string;\n dataOptions: string;\n dataPlanOptions: string;\n }): string {\n return `\n <style>${buildStyles(p.tokens)}</style>\n\n <div class=\"rc-header\">\n <div class=\"rc-header-top\">\n <div class=\"rc-title\">\n <h2 id=\"rc-main-title\">Auto Topup Subscription</h2>\n <p id=\"rc-desc\">Automate your airtime & data recharge</p>\n </div>\n <button class=\"rc-close-btn\" id=\"rc-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"rc-account-bar\">\n <div class=\"rc-dot\"></div>\n <div>\n <span class=\"rc-acc-label\">Your Phone Number</span>\n <span class=\"rc-acc-no\">${formatPhone(p.msisdn)}</span>\n </div>\n </div>\n </div>\n\n <div class=\"rc-content\">\n <div class=\"rc-group\">\n <p class=\"rc-group-title\">Registration Details</p>\n <div class=\"rc-row\" id=\"row-beneficiary\">\n <div class=\"rc-field\" id=\"field-name\">\n <input type=\"text\" id=\"rc-name\" placeholder=\" \" value=\"${p.prefilledName}\" autocomplete=\"name\">\n <label>Full Name</label>\n </div>\n <div class=\"rc-field hidden\" id=\"field-msisdn\">\n <input type=\"tel\" id=\"rc-msisdn-input\" placeholder=\" \" autocomplete=\"tel\">\n <label>Phone Number</label>\n </div>\n </div>\n </div>\n\n <div class=\"rc-group\" id=\"group-type-selector\">\n <p class=\"rc-group-title\">Subscription Type</p>\n <div class=\"rc-field has-val\">\n <select id=\"rc-type-select\">\n <option value=\"airtime\">Airtime Only</option>\n <option value=\"data\">Data Only</option>\n <option value=\"both\">Both Airtime & Data</option>\n </select>\n <label>Automation Mode</label>\n </div>\n </div>\n\n <hr class=\"rc-divider\">\n\n <div id=\"section-airtime\" class=\"section-animate\">\n <p class=\"rc-group-title\">Airtime Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"airtimeThresholdId\">${p.airtimeOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field\">\n <input type=\"tel\" id=\"airtimeTopupValue\" placeholder=\" \">\n <label>Amount (₦)</label>\n <small>₦${p.airtimeMin} – ₦${p.airtimeMax}</small>\n </div>\n </div>\n </div>\n\n <div id=\"section-data\" class=\"hidden section-animate\">\n <p class=\"rc-group-title\">Data Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"dataThresholdId\">${p.dataOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field has-val\">\n <select id=\"dataTopupValue\">${p.dataPlanOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Select Plan</label>\n </div>\n </div>\n </div>\n\n <div id=\"section-dependent-controls\" class=\"hidden\">\n <hr class=\"rc-divider\">\n <p class=\"rc-group-title\">Spending Controls</p>\n <div class=\"rc-row\" id=\"row-spending\">\n <div class=\"rc-field\" id=\"field-airtime-max\">\n <input type=\"tel\" id=\"rc-airtime-max\" placeholder=\" \">\n <label>Airtime Limit (₦)</label>\n </div>\n <div class=\"rc-field\" id=\"field-data-max\">\n <input type=\"tel\" id=\"rc-data-max\" placeholder=\" \">\n <label>Data Limit (₦)</label>\n </div>\n </div>\n </div>\n\n <button class=\"rc-submit-btn\" id=\"rc-submit\">Activate Subscription</button>\n\n <div id=\"rc-dependent-link-wrap\" class=\"rc-link-wrap hidden\">\n Or <a id=\"rc-switch-to-dep\">add a dependent instead</a>\n </div>\n </div>\n\n <div class=\"rc-footer\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"/>\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>\n </svg>\n Secured by <strong> Retailcode</strong>\n </div>\n `;\n }\n}\n","// Lazy-loads SweetAlert2 from CDN on first call.\n// The Swal reference is typed loosely so we don't need @types/sweetalert2.\n\ndeclare global {\n interface Window {\n Swal: SwalStatic;\n }\n}\n\ninterface SwalStatic {\n fire(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }>;\n}\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function loadSwal(): Promise<void> {\n if (window.Swal) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css';\n document.head.appendChild(link);\n\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11';\n script.onload = () => resolve();\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\nexport function swal(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }> {\n return window.Swal.fire(opts);\n}\n","// Bridges for closing the widget across all host environments.\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: { postMessage(msg: string): void };\n webkit?: { messageHandlers?: { retailcode?: { postMessage(msg: unknown): void } } };\n Android?: { close?(): void };\n RetailcodeFlutter?: { postMessage(msg: string): void }; // Flutter JavascriptChannel\n }\n}\n\nexport function updateUrlStatus(status: 'successful' | 'failed'): void {\n const url = new URL(window.location.href);\n url.searchParams.set('status', status);\n window.history.replaceState({}, '', url);\n}\n\nexport function closeWebview(\n onClose?: (r: { closed: true }) => void,\n unmount?: () => void,\n success = false,\n): void {\n const url = new URL(window.location.href);\n url.searchParams.set('isClose', 'true');\n window.history.replaceState({}, '', url);\n\n const isNative = !!(\n window.ReactNativeWebView ||\n window.RetailcodeFlutter ||\n window.webkit?.messageHandlers?.retailcode ||\n window.Android?.close\n );\n\n // In native WebView contexts the host app dismisses the whole view, so\n // clearing the DOM first causes a black-screen flash. Only unmount for\n // plain browser usage where there is no native dismiss.\n if (!isNative) unmount?.();\n\n onClose?.({ closed: true });\n\n // Include success flag so native apps know whether to fire onSuccess\n // before dismissing — this way the dialog is always fully visible first.\n const msg = JSON.stringify({ action: 'close', success });\n\n if (window.ReactNativeWebView) {\n window.ReactNativeWebView.postMessage(msg);\n } else if (window.RetailcodeFlutter) {\n window.RetailcodeFlutter.postMessage(msg);\n } else if (window.webkit?.messageHandlers?.retailcode) {\n window.webkit.messageHandlers.retailcode.postMessage({ action: 'close', success });\n } else if (window.Android?.close) {\n window.Android.close();\n }\n}\n","import { mix, rgba } from '@auto-topup/core';\n\nexport interface StyleTokens {\n accent: string;\n accentHover: string;\n accentFocus: string;\n accentShadow: string;\n fontFamily: string;\n}\n\nexport function buildTokens(accent: string, fontFamily: string): StyleTokens {\n return {\n accent,\n accentHover: mix(accent, -10),\n accentFocus: rgba(accent, 0.15),\n accentShadow: rgba(accent, 0.25),\n fontFamily,\n };\n}\n\nexport function buildStyles(t: StyleTokens): string {\n return `\n @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :host {\n display: flex;\n justify-content: center;\n min-height: 100vh;\n font-family: ${t.fontFamily};\n background: #FFFFFF;\n }\n @media (min-width: 560px) {\n :host { background: rgba(0,0,0,.50); align-items: center; padding: 20px; }\n }\n\n .rc-modal {\n width: 100%; max-width: 440px; background: #FFFFFF; overflow: hidden;\n animation: modalSlide .35s cubic-bezier(.22,1,.36,1) both;\n border-radius: 0; box-shadow: none;\n }\n @media (min-width: 560px) {\n .rc-modal {\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,.06), 0 12px 32px rgba(0,0,0,.12), 0 32px 64px rgba(0,0,0,.08);\n }\n }\n @keyframes modalSlide { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:none; } }\n\n .rc-header { padding: 24px 20px 14px; }\n .rc-header-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }\n .rc-title h2 { font-size: 19px; font-weight: 700; color: #111827; letter-spacing: -.3px; }\n .rc-title p { font-size: 13px; color: #6B7280; margin-top: 3px; line-height: 1.4; }\n\n .rc-close-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; background: #F3F4F6; border: none;\n border-radius: 6px; color: #6B7280; cursor: pointer; transition: background .15s; flex-shrink: 0;\n }\n .rc-close-btn:hover { background: #E5E7EB; }\n\n .rc-account-bar {\n display: flex; align-items: center; background: ${t.accent};\n padding: 10px 14px; border-radius: 6px; color: #fff;\n }\n .rc-dot { width: 8px; height: 8px; background: #4ADE80; border-radius: 50%; margin-right: 10px; box-shadow: 0 0 0 2px rgba(74,222,128,.3); flex-shrink: 0; }\n .rc-acc-label { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .65; display: block; margin-bottom: 1px; }\n .rc-acc-no { font-size: 14px; font-weight: 700; letter-spacing: .03em; }\n\n .rc-content { padding: 20px 20px 16px; }\n .rc-group { margin-bottom: 14px; }\n .rc-group-title { font-size: 10px; font-weight: 700; color: #9CA3AF; text-transform: uppercase; letter-spacing: .09em; margin-bottom: 10px; }\n\n .rc-field { position: relative; margin-bottom: 10px; }\n .rc-field:last-child { margin-bottom: 0; }\n .rc-field label { position: absolute; left: 12px; top: 16px; font-size: 13.5px; color: #9CA3AF; transition: all .16s cubic-bezier(.4,0,.2,1); pointer-events: none; transform-origin: left top; }\n .rc-field input, .rc-field select {\n width: 100%; height: 52px; padding: 20px 12px 6px;\n background: #F9FAFB; border: 1.5px solid #E5E7EB; border-radius: 6px;\n font-family: inherit; font-size: 14px; font-weight: 500; color: #111827;\n outline: none; transition: border-color .18s, box-shadow .18s, background .18s;\n appearance: none; -webkit-appearance: none;\n }\n .rc-field select {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px; cursor: pointer;\n }\n .rc-field input:focus, .rc-field select:focus {\n border-color: ${t.accent}; background: #FFFFFF; box-shadow: 0 0 0 3px ${t.accentFocus};\n }\n .rc-field.has-val label, .rc-field input:focus ~ label, .rc-field select:focus ~ label {\n top: 7px; font-size: 10.5px; font-weight: 700; color: ${t.accent};\n }\n .rc-field small { display: block; margin-top: 4px; font-size: 11px; color: #9CA3AF; }\n\n .rc-row { display: grid; gap: 10px; margin-bottom: 10px; }\n .rc-row .rc-field { margin-bottom: 0; }\n .rc-divider { height: 1px; background: #F3F4F6; margin: 16px 0; border: none; }\n .section-animate { animation: fadeIn .25s ease-out; }\n @keyframes fadeIn { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }\n\n .rc-link-wrap { margin-top: 12px; text-align: center; font-size: 13px; color: #9CA3AF; }\n .rc-link-wrap a { color: ${t.accent}; text-decoration: none; font-weight: 600; cursor: pointer; }\n .rc-link-wrap a:hover { text-decoration: underline; }\n\n .rc-submit-btn {\n width: 100%; height: 52px; background: ${t.accent}; color: #FFFFFF; border: none;\n border-radius: 6px; font-family: inherit; font-size: 15px; font-weight: 700;\n cursor: pointer; letter-spacing: .01em; margin-top: 6px;\n box-shadow: 0 4px 12px ${t.accentShadow};\n transition: background .18s, transform .1s, box-shadow .18s;\n }\n .rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${rgba(t.accent, 0.35)}; }\n .rc-submit-btn:active:not(:disabled) { transform: translateY(0); }\n .rc-submit-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }\n\n .rc-spinner-ring {\n width: 48px; height: 48px;\n border: 4px solid ${rgba(t.accent, 0.18)};\n border-top-color: ${t.accent};\n border-radius: 50%;\n animation: rc-spin .75s linear infinite;\n }\n @keyframes rc-spin { to { transform: rotate(360deg); } }\n\n .rc-footer {\n text-align: center; padding: 0 20px 18px; font-size: 11px; color: #D1D5DB;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n }\n .rc-footer svg { opacity: .5; }\n .hidden { display: none !important; }\n `;\n}\n"],"mappings":"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","names":["resolveAccent","_a","_b","resolveAccent"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/widget.ts","../src/dialog.ts","../src/webview.ts","../src/styles.ts"],"sourcesContent":["import { resolveAccent } from '@auto-topup/core';\nimport { TopupWidget } from './widget.js';\nimport type { TopupCallbacks } from '@auto-topup/core';\n\nexport type { TopupCallbacks };\nexport { TopupWidget };\n\nexport interface CreateConfig extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\ntheme?: { accent?: string; fontFamily?: string };\n}\n\nexport const RetailcodeTopup = {\n create(config: CreateConfig) {\n const widget = new TopupWidget({\n ...config,\n theme: {\n ...config.theme,\n accent: resolveAccent(config.theme?.accent),\n },\n });\n return {\n mount: () => widget.mount(),\n };\n },\n};\n","import {\n RetailcodeApiClient,\n RetailcodeApiError,\n TopupApiConfig,\n TopupCallbacks,\n resolveAccent,\n formatPhone,\n isValidMsisdn,\n isValidAmount,\n} from '@auto-topup/core';\nimport { loadSwal, swal } from './dialog.js';\nimport { closeWebview, updateUrlStatus } from './webview.js';\nimport { buildTokens, buildStyles } from './styles.js';\n\nfunction showTermsDialog(opts: {\n html: string;\n accent: string;\n fontFamily: string;\n}): Promise<boolean> {\n return new Promise((resolve) => {\n const overlay = document.createElement('div');\n overlay.style.cssText =\n 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;' +\n 'align-items:center;justify-content:center;z-index:99999;padding:16px;box-sizing:border-box;';\n\n const card = document.createElement('div');\n card.style.cssText =\n `background:#fff;border-radius:12px;width:100%;max-width:480px;` +\n `max-height:85vh;display:flex;flex-direction:column;font-family:${opts.fontFamily};`;\n\n const title = document.createElement('div');\n title.style.cssText =\n 'padding:20px 24px 12px;font-size:18px;font-weight:700;color:#111827;text-align:center;flex-shrink:0;';\n title.textContent = 'Terms & Conditions';\n\n const content = document.createElement('div');\n content.style.cssText =\n 'flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;' +\n 'touch-action:pan-y;overscroll-behavior:contain;' +\n 'padding:0 24px 16px;font-size:13px;line-height:1.65;color:#374151;';\n content.innerHTML = opts.html;\n\n const actions = document.createElement('div');\n actions.style.cssText =\n 'display:flex;gap:12px;padding:16px 24px;flex-shrink:0;border-top:1px solid #F3F4F6;';\n\n const dismiss = (agreed: boolean) => {\n document.body.style.overflow = '';\n document.body.removeChild(overlay);\n resolve(agreed);\n };\n\n const decline = document.createElement('button');\n decline.style.cssText =\n 'flex:1;padding:12px;border-radius:8px;border:none;background:#9CA3AF;' +\n 'color:#fff;font-size:14px;font-weight:600;cursor:pointer;';\n decline.textContent = 'Decline';\n decline.onclick = () => dismiss(false);\n\n const agree = document.createElement('button');\n agree.style.cssText =\n `flex:2;padding:12px;border-radius:8px;border:none;background:${opts.accent};` +\n 'color:#fff;font-size:14px;font-weight:600;cursor:pointer;';\n agree.textContent = 'I Agree & Continue';\n agree.onclick = () => dismiss(true);\n\n actions.appendChild(decline);\n actions.appendChild(agree);\n card.appendChild(title);\n card.appendChild(content);\n card.appendChild(actions);\n overlay.appendChild(card);\n document.body.style.overflow = 'hidden';\n document.body.appendChild(overlay);\n });\n}\n\ninterface WidgetOptions extends TopupCallbacks {\n publicKey: string;\n msisdn: string;\n container: string;\n theme?: { accent?: string; fontFamily?: string };\n}\n\nexport class TopupWidget {\n private readonly client: RetailcodeApiClient;\n private readonly opts: WidgetOptions;\n\n constructor(opts: WidgetOptions) {\n this.opts = opts;\n this.client = new RetailcodeApiClient(opts.publicKey);\n }\n\n async mount(): Promise<void> {\n const { msisdn, container, onClose, theme = {} } = this.opts;\n\n const target = document.querySelector(container);\n if (!target) return;\n\n const accent = resolveAccent(theme.accent);\n const fontFamily = theme.fontFamily ?? \"'DM Sans', system-ui, sans-serif\";\n const tokens = buildTokens(accent, fontFamily);\n\n let shadow: ShadowRoot | null = null;\n\n const showInitError = (message: string) => {\n const html = `\n <div style=\"font-family:${fontFamily};padding:24px;border:1.5px solid #FCA5A5;background:#FEF2F2;border-radius:8px;color:#991B1B;text-align:center;max-width:440px;margin:40px auto;\">\n <div style=\"font-weight:700;font-size:15px;margin-bottom:4px;\">SDK failed to initialize</div>\n <div style=\"font-size:13px;opacity:0.8;\">${message}</div>\n </div>`;\n if (shadow) shadow.innerHTML = html;\n else (target as HTMLElement).innerHTML = html;\n };\n\n if (!isValidMsisdn(msisdn)) {\n showInitError('Subscriber phone number is not valid.');\n return;\n }\n\n shadow = target.attachShadow({ mode: 'open' });\n this.renderSpinner(shadow, tokens);\n\n let cfg: TopupApiConfig;\n try {\n cfg = await this.client.fetchConfig(msisdn);\n } catch (e) {\n const msg =\n e instanceof RetailcodeApiError ? e.message : 'Could not connect to the activation server.';\n showInitError(msg);\n return;\n }\n\n shadow.innerHTML = '';\n\n const {\n name: prefilledName = '',\n subscribedAirtime = false,\n subscribedData = false,\n airtimethresholds = {},\n airtimeMin = 0,\n airtimeMax = 10000,\n dataThresholds = {},\n dataPlans = [],\n terms = null,\n } = cfg;\n\n const isFullSubscriber = subscribedAirtime && subscribedData;\n const isPartialSubscriber = (subscribedAirtime || subscribedData) && !isFullSubscriber;\n const isBrandNew = !subscribedAirtime && !subscribedData;\n\n // Terms gate for brand-new users — shown before loading SweetAlert2 CDN\n if (isBrandNew && terms) {\n const agreed = await showTermsDialog({\n html: typeof terms === 'string' ? terms.replace(/\\n/g, '<br>') : '',\n accent,\n fontFamily,\n });\n if (!agreed) {\n closeWebview(onClose);\n return;\n }\n }\n\n // Load SweetAlert2 for form validation/success toasts\n await loadSwal();\n\n // Build option HTML strings\n const airtimeOptions = Object.entries(airtimethresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ₦${k}</option>`)\n .join('');\n const dataOptions = Object.entries(dataThresholds)\n .map(([k, id]) => `<option value=\"${id}\">Below ${k}</option>`)\n .join('');\n const dataPlanOptions = dataPlans\n .map(p => `<option value=\"${p.productId}\">${p.allowance} — ₦${p.price}</option>`)\n .join('');\n\n const modal = document.createElement('div');\n modal.className = 'rc-modal';\n modal.innerHTML = this.buildModalHtml({\n tokens,\n msisdn,\n prefilledName,\n airtimeMin,\n airtimeMax,\n airtimeOptions,\n dataOptions,\n dataPlanOptions,\n });\n\n shadow.appendChild(modal);\n\n // ── Refs ────────────────────────────────────────────────────────────────\n const $ = <T extends Element = HTMLElement>(id: string) =>\n shadow!.getElementById(id) as T | null;\n\n const typeSelect = $<HTMLSelectElement>('rc-type-select')!;\n const airtimeSec = $('section-airtime')!;\n const dataSec = $('section-data')!;\n const titleText = $('rc-main-title')!;\n const descText = $('rc-desc')!;\n const submitBtn = $<HTMLButtonElement>('rc-submit')!;\n const msisdnField = $('field-msisdn')!;\n const depLinkWrap = $('rc-dependent-link-wrap')!;\n const depControls = $('section-dependent-controls')!;\n const typeSelectorGrp = $('group-type-selector')!;\n const beneficiaryRow = $('row-beneficiary')!;\n const spendingRow = $('row-spending')!;\n const airtimeMaxField = $('field-airtime-max')!;\n const dataMaxField = $('field-data-max')!;\n\n let forceDependentView = false;\n\n const refreshUI = () => {\n const isDep = isFullSubscriber || forceDependentView;\n const mode = typeSelect.value;\n\n if (isDep) {\n titleText.innerText = 'Add Dependent';\n descText.innerText = 'Configure subscription for someone else';\n msisdnField.classList.remove('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr 1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.remove('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n airtimeMaxField.classList.toggle('hidden', mode === 'data');\n dataMaxField.classList.toggle('hidden', mode === 'airtime');\n spendingRow.style.gridTemplateColumns = mode === 'both' ? '1fr 1fr' : '1fr';\n } else if (isPartialSubscriber) {\n titleText.innerText = 'Complete Profile';\n descText.innerText = 'Finish your Auto Topup subscription';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.add('hidden');\n depLinkWrap.classList.remove('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', subscribedAirtime);\n dataSec.classList.toggle('hidden', subscribedData);\n } else {\n titleText.innerText = 'Auto Topup Subscription';\n descText.innerText = 'Automate your airtime & data recharge';\n msisdnField.classList.add('hidden');\n beneficiaryRow.style.gridTemplateColumns = '1fr';\n typeSelectorGrp.classList.remove('hidden');\n depLinkWrap.classList.add('hidden');\n depControls.classList.add('hidden');\n airtimeSec.classList.toggle('hidden', mode === 'data');\n dataSec.classList.toggle('hidden', mode === 'airtime');\n }\n };\n\n refreshUI();\n\n // ── Event wiring ────────────────────────────────────────────────────────\n const unmount = () => { shadow!.innerHTML = ''; };\n\n typeSelect.addEventListener('change', refreshUI);\n $('rc-close')!.addEventListener('click', () => closeWebview(onClose, unmount));\n\n $('rc-switch-to-dep')!.addEventListener('click', (e) => {\n e.preventDefault();\n forceDependentView = true;\n ($<HTMLInputElement>('rc-name'))!.value = '';\n refreshUI();\n });\n\n $<HTMLInputElement>('rc-name')!.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/[^a-zA-Z\\s]/g, '');\n });\n\n ['rc-msisdn-input', 'airtimeTopupValue', 'rc-airtime-max', 'rc-data-max'].forEach(id => {\n $<HTMLInputElement>(id)?.addEventListener('input', e => {\n const el = e.target as HTMLInputElement;\n el.value = el.value.replace(/\\D/g, '');\n });\n });\n\n shadow.querySelectorAll<HTMLInputElement | HTMLSelectElement>('.rc-field input, .rc-field select')\n .forEach(el => {\n const sync = () => el.closest('.rc-field')!.classList.toggle('has-val', el.value !== '');\n el.addEventListener('input', sync);\n el.addEventListener('change', sync);\n sync();\n });\n\n // ── Submit ──────────────────────────────────────────────────────────────\n submitBtn.addEventListener('click', async () => {\n const getVal = (id: string) =>\n ($<HTMLInputElement>(id))?.value.trim() ?? '';\n\n const isDep = isFullSubscriber || forceDependentView;\n const beneficiary = isDep ? getVal('rc-msisdn-input') : msisdn;\n\n if (!airtimeSec.classList.contains('hidden')) {\n if (!isValidAmount(getVal('airtimeTopupValue'), airtimeMin, airtimeMax)) {\n swal({ icon: 'warning', title: 'Invalid Amount', text: `Enter ₦${airtimeMin} – ₦${airtimeMax}.`, confirmButtonColor: accent });\n return;\n }\n }\n if (isDep && !beneficiary) {\n swal({ icon: 'warning', title: 'Wait!', text: 'Enter the dependent phone number.', confirmButtonColor: accent });\n return;\n }\n\n submitBtn.disabled = true;\n submitBtn.innerText = 'Processing…';\n\n const common = { network: 'MTN', msisdn: beneficiary, name: getVal('rc-name') };\n\n try {\n if (!airtimeSec.classList.contains('hidden')) {\n await this.client.subscribeAirtime({\n ...common,\n airtimeThresholdId: getVal('airtimeThresholdId'),\n airtimeTopupValue: getVal('airtimeTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-airtime-max') }),\n });\n }\n if (!dataSec.classList.contains('hidden')) {\n await this.client.subscribeData({\n ...common,\n dataThresholdId: getVal('dataThresholdId'),\n dataTopupValue: getVal('dataTopupValue'),\n ...(isDep && { customerMsisdn: msisdn, monthlyMaximum: getVal('rc-data-max') }),\n });\n }\n\n updateUrlStatus('successful');\n this.opts.onSuccess?.({ success: true });\n // Use didClose so we notify the native host AFTER SweetAlert2 finishes\n // its close animation — prevents the black-screen flash that occurs when\n // Flutter/iOS dismisses the sheet while the backdrop is still fading out.\n swal({\n icon: 'success',\n title: 'Subscription Active!',\n text: 'Your auto top-up is now enabled.',\n confirmButtonColor: accent,\n didClose: () => closeWebview(onClose, unmount, true),\n });\n } catch (err) {\n updateUrlStatus('failed');\n swal({ icon: 'error', title: 'Oops!', text: (err as Error).message, confirmButtonColor: accent });\n submitBtn.disabled = false;\n submitBtn.innerText = 'Activate Subscription';\n }\n });\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private renderSpinner(shadow: ShadowRoot, tokens: ReturnType<typeof buildTokens>): void {\n const el = document.createElement('div');\n el.innerHTML = `\n <style>\n :host { display:flex; align-items:center; justify-content:center; min-height:100vh; background:#fff; }\n @media (min-width:560px) { :host { background:rgba(0,0,0,.50); } }\n ${buildStyles(tokens).match(/\\.rc-spinner-ring[\\s\\S]*?@keyframes rc-spin[\\s\\S]*?}/)?.[0] ?? ''}\n </style>\n <div class=\"rc-spinner-ring\"></div>`;\n shadow.appendChild(el);\n }\n\n private buildModalHtml(p: {\n tokens: ReturnType<typeof buildTokens>;\n msisdn: string;\n prefilledName: string;\n airtimeMin: number;\n airtimeMax: number;\n airtimeOptions: string;\n dataOptions: string;\n dataPlanOptions: string;\n }): string {\n return `\n <style>${buildStyles(p.tokens)}</style>\n\n <div class=\"rc-header\">\n <div class=\"rc-header-top\">\n <div class=\"rc-title\">\n <h2 id=\"rc-main-title\">Auto Topup Subscription</h2>\n <p id=\"rc-desc\">Automate your airtime & data recharge</p>\n </div>\n <button class=\"rc-close-btn\" id=\"rc-close\" aria-label=\"Close\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"rc-account-bar\">\n <div class=\"rc-dot\"></div>\n <div>\n <span class=\"rc-acc-label\">Your Phone Number</span>\n <span class=\"rc-acc-no\">${formatPhone(p.msisdn)}</span>\n </div>\n </div>\n </div>\n\n <div class=\"rc-content\">\n <div class=\"rc-group\">\n <p class=\"rc-group-title\">Registration Details</p>\n <div class=\"rc-row\" id=\"row-beneficiary\">\n <div class=\"rc-field\" id=\"field-name\">\n <input type=\"text\" id=\"rc-name\" placeholder=\" \" value=\"${p.prefilledName}\" autocomplete=\"name\">\n <label>Full Name</label>\n </div>\n <div class=\"rc-field hidden\" id=\"field-msisdn\">\n <input type=\"tel\" id=\"rc-msisdn-input\" placeholder=\" \" autocomplete=\"tel\">\n <label>Phone Number</label>\n </div>\n </div>\n </div>\n\n <div class=\"rc-group\" id=\"group-type-selector\">\n <p class=\"rc-group-title\">Subscription Type</p>\n <div class=\"rc-field has-val\">\n <select id=\"rc-type-select\">\n <option value=\"airtime\">Airtime Only</option>\n <option value=\"data\">Data Only</option>\n <option value=\"both\">Both Airtime & Data</option>\n </select>\n <label>Automation Mode</label>\n </div>\n </div>\n\n <hr class=\"rc-divider\">\n\n <div id=\"section-airtime\" class=\"section-animate\">\n <p class=\"rc-group-title\">Airtime Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"airtimeThresholdId\">${p.airtimeOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field\">\n <input type=\"tel\" id=\"airtimeTopupValue\" placeholder=\" \">\n <label>Amount (₦)</label>\n <small>₦${p.airtimeMin} – ₦${p.airtimeMax}</small>\n </div>\n </div>\n </div>\n\n <div id=\"section-data\" class=\"hidden section-animate\">\n <p class=\"rc-group-title\">Data Setup</p>\n <div class=\"rc-row\" style=\"grid-template-columns:1fr 1fr\">\n <div class=\"rc-field has-val\">\n <select id=\"dataThresholdId\">${p.dataOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Recharge when</label>\n </div>\n <div class=\"rc-field has-val\">\n <select id=\"dataTopupValue\">${p.dataPlanOptions || '<option value=\"\">Loading…</option>'}</select>\n <label>Select Plan</label>\n </div>\n </div>\n </div>\n\n <div id=\"section-dependent-controls\" class=\"hidden\">\n <hr class=\"rc-divider\">\n <p class=\"rc-group-title\">Spending Controls</p>\n <div class=\"rc-row\" id=\"row-spending\">\n <div class=\"rc-field\" id=\"field-airtime-max\">\n <input type=\"tel\" id=\"rc-airtime-max\" placeholder=\" \">\n <label>Airtime Limit (₦)</label>\n </div>\n <div class=\"rc-field\" id=\"field-data-max\">\n <input type=\"tel\" id=\"rc-data-max\" placeholder=\" \">\n <label>Data Limit (₦)</label>\n </div>\n </div>\n </div>\n\n <button class=\"rc-submit-btn\" id=\"rc-submit\">Activate Subscription</button>\n\n <div id=\"rc-dependent-link-wrap\" class=\"rc-link-wrap hidden\">\n Or <a id=\"rc-switch-to-dep\">add a dependent instead</a>\n </div>\n </div>\n\n <div class=\"rc-footer\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\"/>\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>\n </svg>\n Secured by <strong> Retailcode</strong>\n </div>\n `;\n }\n}\n","// Lazy-loads SweetAlert2 from CDN on first call.\n// The Swal reference is typed loosely so we don't need @types/sweetalert2.\n\ndeclare global {\n interface Window {\n Swal: SwalStatic;\n }\n}\n\ninterface SwalStatic {\n fire(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }>;\n}\n\nlet loadPromise: Promise<void> | null = null;\n\nexport function loadSwal(): Promise<void> {\n if (window.Swal) return Promise.resolve();\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve) => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css';\n document.head.appendChild(link);\n\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11';\n script.onload = () => resolve();\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\nexport function swal(opts: Record<string, unknown>): Promise<{ isConfirmed: boolean }> {\n return window.Swal.fire(opts);\n}\n","// Bridges for closing the widget across all host environments.\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: { postMessage(msg: string): void };\n webkit?: { messageHandlers?: { retailcode?: { postMessage(msg: unknown): void } } };\n Android?: { close?(): void };\n RetailcodeFlutter?: { postMessage(msg: string): void }; // Flutter JavascriptChannel\n }\n}\n\nexport function updateUrlStatus(status: 'successful' | 'failed'): void {\n const url = new URL(window.location.href);\n url.searchParams.set('status', status);\n window.history.replaceState({}, '', url);\n}\n\nexport function closeWebview(\n onClose?: (r: { closed: true }) => void,\n unmount?: () => void,\n success = false,\n): void {\n const url = new URL(window.location.href);\n url.searchParams.set('isClose', 'true');\n window.history.replaceState({}, '', url);\n\n const isNative = !!(\n window.ReactNativeWebView ||\n window.RetailcodeFlutter ||\n window.webkit?.messageHandlers?.retailcode ||\n window.Android?.close\n );\n\n // In native WebView contexts the host app dismisses the whole view, so\n // clearing the DOM first causes a black-screen flash. Only unmount for\n // plain browser usage where there is no native dismiss.\n if (!isNative) unmount?.();\n\n onClose?.({ closed: true });\n\n // Include success flag so native apps know whether to fire onSuccess\n // before dismissing — this way the dialog is always fully visible first.\n const msg = JSON.stringify({ action: 'close', success });\n\n if (window.ReactNativeWebView) {\n window.ReactNativeWebView.postMessage(msg);\n } else if (window.RetailcodeFlutter) {\n window.RetailcodeFlutter.postMessage(msg);\n } else if (window.webkit?.messageHandlers?.retailcode) {\n window.webkit.messageHandlers.retailcode.postMessage({ action: 'close', success });\n } else if (window.Android?.close) {\n window.Android.close();\n }\n}\n","import { mix, rgba } from '@auto-topup/core';\n\nexport interface StyleTokens {\n accent: string;\n accentHover: string;\n accentFocus: string;\n accentShadow: string;\n fontFamily: string;\n}\n\nexport function buildTokens(accent: string, fontFamily: string): StyleTokens {\n return {\n accent,\n accentHover: mix(accent, -10),\n accentFocus: rgba(accent, 0.15),\n accentShadow: rgba(accent, 0.25),\n fontFamily,\n };\n}\n\nexport function buildStyles(t: StyleTokens): string {\n return `\n @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :host {\n display: flex;\n justify-content: center;\n min-height: 100vh;\n font-family: ${t.fontFamily};\n background: #FFFFFF;\n }\n @media (min-width: 560px) {\n :host { background: rgba(0,0,0,.50); align-items: center; padding: 20px; }\n }\n\n .rc-modal {\n width: 100%; max-width: 440px; background: #FFFFFF; overflow: hidden;\n animation: modalSlide .35s cubic-bezier(.22,1,.36,1) both;\n border-radius: 0; box-shadow: none;\n }\n @media (min-width: 560px) {\n .rc-modal {\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,.06), 0 12px 32px rgba(0,0,0,.12), 0 32px 64px rgba(0,0,0,.08);\n }\n }\n @keyframes modalSlide { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:none; } }\n\n .rc-header { padding: 24px 20px 14px; }\n .rc-header-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }\n .rc-title h2 { font-size: 19px; font-weight: 700; color: #111827; letter-spacing: -.3px; }\n .rc-title p { font-size: 13px; color: #6B7280; margin-top: 3px; line-height: 1.4; }\n\n .rc-close-btn {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; background: #F3F4F6; border: none;\n border-radius: 6px; color: #6B7280; cursor: pointer; transition: background .15s; flex-shrink: 0;\n }\n .rc-close-btn:hover { background: #E5E7EB; }\n\n .rc-account-bar {\n display: flex; align-items: center; background: ${t.accent};\n padding: 10px 14px; border-radius: 6px; color: #fff;\n }\n .rc-dot { width: 8px; height: 8px; background: #4ADE80; border-radius: 50%; margin-right: 10px; box-shadow: 0 0 0 2px rgba(74,222,128,.3); flex-shrink: 0; }\n .rc-acc-label { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .65; display: block; margin-bottom: 1px; }\n .rc-acc-no { font-size: 14px; font-weight: 700; letter-spacing: .03em; }\n\n .rc-content { padding: 20px 20px 16px; }\n .rc-group { margin-bottom: 14px; }\n .rc-group-title { font-size: 10px; font-weight: 700; color: #9CA3AF; text-transform: uppercase; letter-spacing: .09em; margin-bottom: 10px; }\n\n .rc-field { position: relative; margin-bottom: 10px; }\n .rc-field:last-child { margin-bottom: 0; }\n .rc-field label { position: absolute; left: 12px; top: 16px; font-size: 13.5px; color: #9CA3AF; transition: all .16s cubic-bezier(.4,0,.2,1); pointer-events: none; transform-origin: left top; }\n .rc-field input, .rc-field select {\n width: 100%; height: 52px; padding: 20px 12px 6px;\n background: #F9FAFB; border: 1.5px solid #E5E7EB; border-radius: 6px;\n font-family: inherit; font-size: 14px; font-weight: 500; color: #111827;\n outline: none; transition: border-color .18s, box-shadow .18s, background .18s;\n appearance: none; -webkit-appearance: none;\n }\n .rc-field select {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");\n background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px; cursor: pointer;\n }\n .rc-field input:focus, .rc-field select:focus {\n border-color: ${t.accent}; background: #FFFFFF; box-shadow: 0 0 0 3px ${t.accentFocus};\n }\n .rc-field.has-val label, .rc-field input:focus ~ label, .rc-field select:focus ~ label {\n top: 7px; font-size: 10.5px; font-weight: 700; color: ${t.accent};\n }\n .rc-field small { display: block; margin-top: 4px; font-size: 11px; color: #9CA3AF; }\n\n .rc-row { display: grid; gap: 10px; margin-bottom: 10px; }\n .rc-row .rc-field { margin-bottom: 0; }\n .rc-divider { height: 1px; background: #F3F4F6; margin: 16px 0; border: none; }\n .section-animate { animation: fadeIn .25s ease-out; }\n @keyframes fadeIn { from { opacity:0; transform:translateY(4px); } to { opacity:1; transform:none; } }\n\n .rc-link-wrap { margin-top: 12px; text-align: center; font-size: 13px; color: #9CA3AF; }\n .rc-link-wrap a { color: ${t.accent}; text-decoration: none; font-weight: 600; cursor: pointer; }\n .rc-link-wrap a:hover { text-decoration: underline; }\n\n .rc-submit-btn {\n width: 100%; height: 52px; background: ${t.accent}; color: #FFFFFF; border: none;\n border-radius: 6px; font-family: inherit; font-size: 15px; font-weight: 700;\n cursor: pointer; letter-spacing: .01em; margin-top: 6px;\n box-shadow: 0 4px 12px ${t.accentShadow};\n transition: background .18s, transform .1s, box-shadow .18s;\n }\n .rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${rgba(t.accent, 0.35)}; }\n .rc-submit-btn:active:not(:disabled) { transform: translateY(0); }\n .rc-submit-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }\n\n .rc-spinner-ring {\n width: 48px; height: 48px;\n border: 4px solid ${rgba(t.accent, 0.18)};\n border-top-color: ${t.accent};\n border-radius: 50%;\n animation: rc-spin .75s linear infinite;\n }\n @keyframes rc-spin { to { transform: rotate(360deg); } }\n\n .rc-footer {\n text-align: center; padding: 0 20px 18px; font-size: 11px; color: #D1D5DB;\n display: flex; align-items: center; justify-content: center; gap: 5px;\n }\n .rc-footer svg { opacity: .5; }\n .hidden { display: none !important; }\n `;\n}\n"],"mappings":"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","names":["resolveAccent","_a","_b","resolveAccent"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RetailcodeSDK=(()=>{var
|
|
1
|
+
"use strict";var RetailcodeSDK=(()=>{var E=Object.defineProperty,Le=Object.defineProperties,Ae=Object.getOwnPropertyDescriptor,Me=Object.getOwnPropertyDescriptors,$e=Object.getOwnPropertyNames,ie=Object.getOwnPropertySymbols;var oe=Object.prototype.hasOwnProperty,Pe=Object.prototype.propertyIsEnumerable;var ne=(t,e,i)=>e in t?E(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,m=(t,e)=>{for(var i in e||(e={}))oe.call(e,i)&&ne(t,i,e[i]);if(ie)for(var i of ie(e))Pe.call(e,i)&&ne(t,i,e[i]);return t},g=(t,e)=>Le(t,Me(e));var He=(t,e)=>{for(var i in e)E(t,i,{get:e[i],enumerable:!0})},Re=(t,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of $e(e))!oe.call(t,a)&&a!==i&&E(t,a,{get:()=>e[a],enumerable:!(n=Ae(e,a))||n.enumerable});return t};var Oe=t=>Re(E({},"__esModule",{value:!0}),t);var Ve={};He(Ve,{RetailcodeTopup:()=>he});var ze=Object.defineProperty,re=Object.getOwnPropertySymbols,Be=Object.prototype.hasOwnProperty,Ie=Object.prototype.propertyIsEnumerable,ae=(t,e,i)=>e in t?ze(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,se=(t,e)=>{for(var i in e||(e={}))Be.call(e,i)&&ae(t,i,e[i]);if(re)for(var i of re(e))Ie.call(e,i)&&ae(t,i,e[i]);return t},h=class extends Error{constructor(t,e){super(t),this.status=e,this.name="RetailcodeApiError"}},je="https://corporateprodapi.retailcode.com.ng",ce=class{constructor(t){this.publicKey=t,this.baseUrl=je}get authHeaders(){return{Authorization:`Bearer ${this.publicKey}`,Accept:"application/json"}}async fetchConfig(t){var e;let i=await fetch(`${this.baseUrl}/api/v1/auto-topup/public/config/${t}`,{headers:this.authHeaders});if(!i.ok)throw new h("Public API key is invalid or expired.",i.status);let n=await i.json();if(!n.success)throw new h((e=n.message)!=null?e:"Public API key is invalid or expired.");return n.data}async subscribeAirtime(t){var e;let n=await(await fetch(`${this.baseUrl}/api/v1/auto-topup/public/subscribe/airtime`,{method:"POST",headers:se({"Content-Type":"application/json"},this.authHeaders),body:JSON.stringify(t)})).json();if(!n.success)throw new h((e=n.message)!=null?e:"Subscription failed")}async subscribeData(t){var e;let n=await(await fetch(`${this.baseUrl}/api/v1/auto-topup/public/subscribe/data`,{method:"POST",headers:se({"Content-Type":"application/json"},this.authHeaders),body:JSON.stringify(t)})).json();if(!n.success)throw new h((e=n.message)!=null?e:"Subscription failed")}};function le(t){let e=t.replace("#",""),i=e.length===3?e.split("").map(a=>a+a).join(""):e,n=parseInt(i,16);return{r:n>>16&255,g:n>>8&255,b:n&255}}function de(t,e){let{r:i,g:n,b:a}=le(t),l=e>0?255:0,c=Math.abs(e)/100,p=u=>Math.min(255,Math.max(0,Math.round(u))).toString(16).padStart(2,"0");return"#"+p(i+(l-i)*c)+p(n+(l-n)*c)+p(a+(l-a)*c)}function y(t,e){let{r:i,g:n,b:a}=le(t);return`rgba(${i},${n},${a},${e})`}function L(t){return t&&!["null","undefined",""].includes(String(t))?t:"#0057FF"}var De=/^(234[789][01]\d{8}|0[789][01]\d{8})$/;function pe(t){return De.test(t)}function ue(t){let e=t.replace(/\D/g,"");return e.length===11?e.replace(/(\d{4})(\d{3})(\d{4})/,"$1 $2 $3"):t}function me(t,e,i){let n=parseFloat(t);return!isNaN(n)&&n>=e&&n<=i}var A=null;function fe(){return window.Swal?Promise.resolve():A||(A=new Promise(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css",document.head.appendChild(e);let i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/sweetalert2@11",i.onload=()=>t(),document.head.appendChild(i)}),A)}function T(t){return window.Swal.fire(t)}function Y(t){let e=new URL(window.location.href);e.searchParams.set("status",t),window.history.replaceState({},"",e)}function M(t,e,i=!1){var c,p,u,d,f,b;let n=new URL(window.location.href);n.searchParams.set("isClose","true"),window.history.replaceState({},"",n),window.ReactNativeWebView||window.RetailcodeFlutter||(p=(c=window.webkit)==null?void 0:c.messageHandlers)!=null&&p.retailcode||(u=window.Android)!=null&&u.close||e==null||e(),t==null||t({closed:!0});let l=JSON.stringify({action:"close",success:i});window.ReactNativeWebView?window.ReactNativeWebView.postMessage(l):window.RetailcodeFlutter?window.RetailcodeFlutter.postMessage(l):(f=(d=window.webkit)==null?void 0:d.messageHandlers)!=null&&f.retailcode?window.webkit.messageHandlers.retailcode.postMessage({action:"close",success:i}):(b=window.Android)!=null&&b.close&&window.Android.close()}function ge(t,e){return{accent:t,accentHover:de(t,-10),accentFocus:y(t,.15),accentShadow:y(t,.25),fontFamily:e}}function J(t){return`
|
|
2
2
|
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');
|
|
3
3
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
4
4
|
|
|
@@ -89,13 +89,13 @@
|
|
|
89
89
|
box-shadow: 0 4px 12px ${t.accentShadow};
|
|
90
90
|
transition: background .18s, transform .1s, box-shadow .18s;
|
|
91
91
|
}
|
|
92
|
-
.rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${
|
|
92
|
+
.rc-submit-btn:hover:not(:disabled) { background: ${t.accentHover}; transform: translateY(-1px); box-shadow: 0 6px 18px ${y(t.accent,.35)}; }
|
|
93
93
|
.rc-submit-btn:active:not(:disabled) { transform: translateY(0); }
|
|
94
94
|
.rc-submit-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
|
|
95
95
|
|
|
96
96
|
.rc-spinner-ring {
|
|
97
97
|
width: 48px; height: 48px;
|
|
98
|
-
border: 4px solid ${
|
|
98
|
+
border: 4px solid ${y(t.accent,.18)};
|
|
99
99
|
border-top-color: ${t.accent};
|
|
100
100
|
border-radius: 50%;
|
|
101
101
|
animation: rc-spin .75s linear infinite;
|
|
@@ -108,20 +108,17 @@
|
|
|
108
108
|
}
|
|
109
109
|
.rc-footer svg { opacity: .5; }
|
|
110
110
|
.hidden { display: none !important; }
|
|
111
|
-
`}var $=class{constructor(e){this.opts=e,this.client=new
|
|
111
|
+
`}function Ne(t){return new Promise(e=>{let i=document.createElement("div");i.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:99999;padding:16px;box-sizing:border-box;";let n=document.createElement("div");n.style.cssText=`background:#fff;border-radius:12px;width:100%;max-width:480px;max-height:85vh;display:flex;flex-direction:column;font-family:${t.fontFamily};`;let a=document.createElement("div");a.style.cssText="padding:20px 24px 12px;font-size:18px;font-weight:700;color:#111827;text-align:center;flex-shrink:0;",a.textContent="Terms & Conditions";let l=document.createElement("div");l.style.cssText="flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;touch-action:pan-y;overscroll-behavior:contain;padding:0 24px 16px;font-size:13px;line-height:1.65;color:#374151;",l.innerHTML=t.html;let c=document.createElement("div");c.style.cssText="display:flex;gap:12px;padding:16px 24px;flex-shrink:0;border-top:1px solid #F3F4F6;";let p=f=>{document.body.style.overflow="",document.body.removeChild(i),e(f)},u=document.createElement("button");u.style.cssText="flex:1;padding:12px;border-radius:8px;border:none;background:#9CA3AF;color:#fff;font-size:14px;font-weight:600;cursor:pointer;",u.textContent="Decline",u.onclick=()=>p(!1);let d=document.createElement("button");d.style.cssText=`flex:2;padding:12px;border-radius:8px;border:none;background:${t.accent};color:#fff;font-size:14px;font-weight:600;cursor:pointer;`,d.textContent="I Agree & Continue",d.onclick=()=>p(!0),c.appendChild(u),c.appendChild(d),n.appendChild(a),n.appendChild(l),n.appendChild(c),i.appendChild(n),document.body.style.overflow="hidden",document.body.appendChild(i)})}var $=class{constructor(e){this.opts=e,this.client=new ce(e.publicKey)}async mount(){var X;let{msisdn:e,container:i,onClose:n,theme:a={}}=this.opts,l=document.querySelector(i);if(!l)return;let c=L(a.accent),p=(X=a.fontFamily)!=null?X:"'DM Sans', system-ui, sans-serif",u=ge(c,p),d=null,f=o=>{let r=`
|
|
112
112
|
<div style="font-family:${p};padding:24px;border:1.5px solid #FCA5A5;background:#FEF2F2;border-radius:8px;color:#991B1B;text-align:center;max-width:440px;margin:40px auto;">
|
|
113
113
|
<div style="font-weight:700;font-size:15px;margin-bottom:4px;">SDK failed to initialize</div>
|
|
114
|
-
<div style="font-size:13px;opacity:0.8;">${
|
|
115
|
-
</div>`;d?d.innerHTML=
|
|
116
|
-
<div style="text-align:left;font-size:13px;line-height:1.65;color:#374151;max-height:340px;overflow-y:auto;padding-right:4px;font-family:${p};">
|
|
117
|
-
${typeof O=="string"?O.replace(/\n/g,"<br>"):""}
|
|
118
|
-
</div>`,confirmButtonText:"I Agree & Continue",confirmButtonColor:c,showCancelButton:!0,cancelButtonText:"Decline",cancelButtonColor:"#9CA3AF",allowOutsideClick:!1,allowEscapeKey:!1,reverseButtons:!0})).isConfirmed){M(r);return}let Te=Object.entries(xe).map(([n,o])=>`<option value="${o}">Below \u20A6${n}</option>`).join(""),ke=Object.entries(ve).map(([n,o])=>`<option value="${o}">Below ${n}</option>`).join(""),Se=we.map(n=>`<option value="${n.productId}">${n.allowance} \u2014 \u20A6${n.price}</option>`).join(""),B=document.createElement("div");B.className="rc-modal",B.innerHTML=this.buildModalHtml({tokens:m,msisdn:e,prefilledName:be,airtimeMin:P,airtimeMax:H,airtimeOptions:Te,dataOptions:ke,dataPlanOptions:Se}),d.appendChild(B);let s=n=>d.getElementById(n),q=s("rc-type-select"),v=s("section-airtime"),F=s("section-data"),I=s("rc-main-title"),j=s("rc-desc"),w=s("rc-submit"),D=s("field-msisdn"),N=s("rc-dependent-link-wrap"),z=s("section-dependent-controls"),V=s("group-type-selector"),_=s("row-beneficiary"),Fe=s("row-spending"),Le=s("field-airtime-max"),Ae=s("field-data-max"),W=!1,U=()=>{let n=R||W,o=q.value;n?(I.innerText="Add Dependent",j.innerText="Configure subscription for someone else",D.classList.remove("hidden"),_.style.gridTemplateColumns="1fr 1fr",V.classList.remove("hidden"),N.classList.add("hidden"),z.classList.remove("hidden"),v.classList.toggle("hidden",o==="data"),F.classList.toggle("hidden",o==="airtime"),Le.classList.toggle("hidden",o==="data"),Ae.classList.toggle("hidden",o==="airtime"),Fe.style.gridTemplateColumns=o==="both"?"1fr 1fr":"1fr"):ye?(I.innerText="Complete Profile",j.innerText="Finish your Auto Topup subscription",D.classList.add("hidden"),_.style.gridTemplateColumns="1fr",V.classList.add("hidden"),N.classList.remove("hidden"),z.classList.add("hidden"),v.classList.toggle("hidden",k),F.classList.toggle("hidden",S)):(I.innerText="Auto Topup Subscription",j.innerText="Automate your airtime & data recharge",D.classList.add("hidden"),_.style.gridTemplateColumns="1fr",V.classList.remove("hidden"),N.classList.add("hidden"),z.classList.add("hidden"),v.classList.toggle("hidden",o==="data"),F.classList.toggle("hidden",o==="airtime"))};U();let G=()=>{d.innerHTML=""};q.addEventListener("change",U),s("rc-close").addEventListener("click",()=>M(r,G)),s("rc-switch-to-dep").addEventListener("click",n=>{n.preventDefault(),W=!0,s("rc-name").value="",U()}),s("rc-name").addEventListener("input",n=>{let o=n.target;o.value=o.value.replace(/[^a-zA-Z\s]/g,"")}),["rc-msisdn-input","airtimeTopupValue","rc-airtime-max","rc-data-max"].forEach(n=>{var o;(o=s(n))==null||o.addEventListener("input",L=>{let y=L.target;y.value=y.value.replace(/\D/g,"")})}),d.querySelectorAll(".rc-field input, .rc-field select").forEach(n=>{let o=()=>n.closest(".rc-field").classList.toggle("has-val",n.value!=="");n.addEventListener("input",o),n.addEventListener("change",o),o()}),w.addEventListener("click",async()=>{var Z,Q;let n=K=>{var ee,te;return(te=(ee=s(K))==null?void 0:ee.value.trim())!=null?te:""},o=R||W,L=o?n("rc-msisdn-input"):e;if(!v.classList.contains("hidden")&&!me(n("airtimeTopupValue"),P,H)){h({icon:"warning",title:"Invalid Amount",text:`Enter \u20A6${P} \u2013 \u20A6${H}.`,confirmButtonColor:c});return}if(o&&!L){h({icon:"warning",title:"Wait!",text:"Enter the dependent phone number.",confirmButtonColor:c});return}w.disabled=!0,w.innerText="Processing\u2026";let y={network:"MTN",msisdn:L,name:n("rc-name")};try{v.classList.contains("hidden")||await this.client.subscribeAirtime(u(f(u({},y),{airtimeThresholdId:n("airtimeThresholdId"),airtimeTopupValue:n("airtimeTopupValue")}),o&&{customerMsisdn:e,monthlyMaximum:n("rc-airtime-max")})),F.classList.contains("hidden")||await this.client.subscribeData(u(f(u({},y),{dataThresholdId:n("dataThresholdId"),dataTopupValue:n("dataTopupValue")}),o&&{customerMsisdn:e,monthlyMaximum:n("rc-data-max")})),Y("successful"),(Q=(Z=this.opts).onSuccess)==null||Q.call(Z,{success:!0}),h({icon:"success",title:"Subscription Active!",text:"Your auto top-up is now enabled.",confirmButtonColor:c,didClose:()=>M(r,G,!0)})}catch(K){Y("failed"),h({icon:"error",title:"Oops!",text:K.message,confirmButtonColor:c}),w.disabled=!1,w.innerText="Activate Subscription"}})}renderSpinner(e,i){var a,l;let r=document.createElement("div");r.innerHTML=`
|
|
114
|
+
<div style="font-size:13px;opacity:0.8;">${o}</div>
|
|
115
|
+
</div>`;d?d.innerHTML=r:l.innerHTML=r};if(!pe(e)){f("Subscriber phone number is not valid.");return}d=l.attachShadow({mode:"open"}),this.renderSpinner(d,u);let b;try{b=await this.client.fetchConfig(e)}catch(o){let r=o instanceof h?o.message:"Could not connect to the activation server.";f(r);return}d.innerHTML="";let{name:be="",subscribedAirtime:k=!1,subscribedData:F=!1,airtimethresholds:xe={},airtimeMin:P=0,airtimeMax:H=1e4,dataThresholds:ve={},dataPlans:we=[],terms:R=null}=b,O=k&&F,ye=(k||F)&&!O;if(!k&&!F&&R&&!await Ne({html:typeof R=="string"?R.replace(/\n/g,"<br>"):"",accent:c,fontFamily:p})){M(n);return}await fe();let Te=Object.entries(xe).map(([o,r])=>`<option value="${r}">Below \u20A6${o}</option>`).join(""),ke=Object.entries(ve).map(([o,r])=>`<option value="${r}">Below ${o}</option>`).join(""),Fe=we.map(o=>`<option value="${o.productId}">${o.allowance} \u2014 \u20A6${o.price}</option>`).join(""),z=document.createElement("div");z.className="rc-modal",z.innerHTML=this.buildModalHtml({tokens:u,msisdn:e,prefilledName:be,airtimeMin:P,airtimeMax:H,airtimeOptions:Te,dataOptions:ke,dataPlanOptions:Fe}),d.appendChild(z);let s=o=>d.getElementById(o),q=s("rc-type-select"),x=s("section-airtime"),S=s("section-data"),B=s("rc-main-title"),I=s("rc-desc"),v=s("rc-submit"),j=s("field-msisdn"),D=s("rc-dependent-link-wrap"),N=s("section-dependent-controls"),V=s("group-type-selector"),_=s("row-beneficiary"),Se=s("row-spending"),Ce=s("field-airtime-max"),Ee=s("field-data-max"),W=!1,U=()=>{let o=O||W,r=q.value;o?(B.innerText="Add Dependent",I.innerText="Configure subscription for someone else",j.classList.remove("hidden"),_.style.gridTemplateColumns="1fr 1fr",V.classList.remove("hidden"),D.classList.add("hidden"),N.classList.remove("hidden"),x.classList.toggle("hidden",r==="data"),S.classList.toggle("hidden",r==="airtime"),Ce.classList.toggle("hidden",r==="data"),Ee.classList.toggle("hidden",r==="airtime"),Se.style.gridTemplateColumns=r==="both"?"1fr 1fr":"1fr"):ye?(B.innerText="Complete Profile",I.innerText="Finish your Auto Topup subscription",j.classList.add("hidden"),_.style.gridTemplateColumns="1fr",V.classList.add("hidden"),D.classList.remove("hidden"),N.classList.add("hidden"),x.classList.toggle("hidden",k),S.classList.toggle("hidden",F)):(B.innerText="Auto Topup Subscription",I.innerText="Automate your airtime & data recharge",j.classList.add("hidden"),_.style.gridTemplateColumns="1fr",V.classList.remove("hidden"),D.classList.add("hidden"),N.classList.add("hidden"),x.classList.toggle("hidden",r==="data"),S.classList.toggle("hidden",r==="airtime"))};U();let G=()=>{d.innerHTML=""};q.addEventListener("change",U),s("rc-close").addEventListener("click",()=>M(n,G)),s("rc-switch-to-dep").addEventListener("click",o=>{o.preventDefault(),W=!0,s("rc-name").value="",U()}),s("rc-name").addEventListener("input",o=>{let r=o.target;r.value=r.value.replace(/[^a-zA-Z\s]/g,"")}),["rc-msisdn-input","airtimeTopupValue","rc-airtime-max","rc-data-max"].forEach(o=>{var r;(r=s(o))==null||r.addEventListener("input",C=>{let w=C.target;w.value=w.value.replace(/\D/g,"")})}),d.querySelectorAll(".rc-field input, .rc-field select").forEach(o=>{let r=()=>o.closest(".rc-field").classList.toggle("has-val",o.value!=="");o.addEventListener("input",r),o.addEventListener("change",r),r()}),v.addEventListener("click",async()=>{var Z,Q;let o=K=>{var ee,te;return(te=(ee=s(K))==null?void 0:ee.value.trim())!=null?te:""},r=O||W,C=r?o("rc-msisdn-input"):e;if(!x.classList.contains("hidden")&&!me(o("airtimeTopupValue"),P,H)){T({icon:"warning",title:"Invalid Amount",text:`Enter \u20A6${P} \u2013 \u20A6${H}.`,confirmButtonColor:c});return}if(r&&!C){T({icon:"warning",title:"Wait!",text:"Enter the dependent phone number.",confirmButtonColor:c});return}v.disabled=!0,v.innerText="Processing\u2026";let w={network:"MTN",msisdn:C,name:o("rc-name")};try{x.classList.contains("hidden")||await this.client.subscribeAirtime(m(g(m({},w),{airtimeThresholdId:o("airtimeThresholdId"),airtimeTopupValue:o("airtimeTopupValue")}),r&&{customerMsisdn:e,monthlyMaximum:o("rc-airtime-max")})),S.classList.contains("hidden")||await this.client.subscribeData(m(g(m({},w),{dataThresholdId:o("dataThresholdId"),dataTopupValue:o("dataTopupValue")}),r&&{customerMsisdn:e,monthlyMaximum:o("rc-data-max")})),Y("successful"),(Q=(Z=this.opts).onSuccess)==null||Q.call(Z,{success:!0}),T({icon:"success",title:"Subscription Active!",text:"Your auto top-up is now enabled.",confirmButtonColor:c,didClose:()=>M(n,G,!0)})}catch(K){Y("failed"),T({icon:"error",title:"Oops!",text:K.message,confirmButtonColor:c}),v.disabled=!1,v.innerText="Activate Subscription"}})}renderSpinner(e,i){var a,l;let n=document.createElement("div");n.innerHTML=`
|
|
119
116
|
<style>
|
|
120
117
|
:host { display:flex; align-items:center; justify-content:center; min-height:100vh; background:#fff; }
|
|
121
118
|
@media (min-width:560px) { :host { background:rgba(0,0,0,.50); } }
|
|
122
119
|
${(l=(a=J(i).match(/\.rc-spinner-ring[\s\S]*?@keyframes rc-spin[\s\S]*?}/))==null?void 0:a[0])!=null?l:""}
|
|
123
120
|
</style>
|
|
124
|
-
<div class="rc-spinner-ring"></div>`,e.appendChild(
|
|
121
|
+
<div class="rc-spinner-ring"></div>`,e.appendChild(n)}buildModalHtml(e){return`
|
|
125
122
|
<style>${J(e.tokens)}</style>
|
|
126
123
|
|
|
127
124
|
<div class="rc-header">
|
|
@@ -232,4 +229,4 @@
|
|
|
232
229
|
</svg>
|
|
233
230
|
Secured by <strong> Retailcode</strong>
|
|
234
231
|
</div>
|
|
235
|
-
`}};var he={create(t){var i;let e=new $(
|
|
232
|
+
`}};var he={create(t){var i;let e=new $(g(m({},t),{theme:g(m({},t.theme),{accent:L((i=t.theme)==null?void 0:i.accent)})}));return{mount:()=>e.mount()}}};return Oe(Ve);})();
|