@playbasis-ai/qwikcard-sdk 2.3.4

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.
Files changed (106) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/LICENSE +21 -0
  3. package/README.md +267 -0
  4. package/SDK_HANDOVER_GUIDE.md +129 -0
  5. package/dist/PlaybasisProvider.d.ts +19 -0
  6. package/dist/PlaybasisProvider.d.ts.map +1 -0
  7. package/dist/PlaybasisProvider.js +24 -0
  8. package/dist/QwikCardApp.d.ts +9 -0
  9. package/dist/QwikCardApp.d.ts.map +1 -0
  10. package/dist/QwikCardApp.js +210 -0
  11. package/dist/api/client.d.ts +66 -0
  12. package/dist/api/client.d.ts.map +1 -0
  13. package/dist/api/client.js +196 -0
  14. package/dist/components/Badge.d.ts +8 -0
  15. package/dist/components/Badge.d.ts.map +1 -0
  16. package/dist/components/Badge.js +34 -0
  17. package/dist/components/BadgeIcon.d.ts +10 -0
  18. package/dist/components/BadgeIcon.d.ts.map +1 -0
  19. package/dist/components/BadgeIcon.js +51 -0
  20. package/dist/components/Button.d.ts +10 -0
  21. package/dist/components/Button.d.ts.map +1 -0
  22. package/dist/components/Button.js +40 -0
  23. package/dist/components/GradientCard.d.ts +9 -0
  24. package/dist/components/GradientCard.d.ts.map +1 -0
  25. package/dist/components/GradientCard.js +28 -0
  26. package/dist/components/PointsBalance.d.ts +8 -0
  27. package/dist/components/PointsBalance.d.ts.map +1 -0
  28. package/dist/components/PointsBalance.js +85 -0
  29. package/dist/components/ProgressBar.d.ts +8 -0
  30. package/dist/components/ProgressBar.d.ts.map +1 -0
  31. package/dist/components/ProgressBar.js +41 -0
  32. package/dist/components/QuestProgress.d.ts +10 -0
  33. package/dist/components/QuestProgress.d.ts.map +1 -0
  34. package/dist/components/QuestProgress.js +94 -0
  35. package/dist/components/RadialGauge.d.ts +8 -0
  36. package/dist/components/RadialGauge.d.ts.map +1 -0
  37. package/dist/components/RadialGauge.js +53 -0
  38. package/dist/components/RewardCard.d.ts +10 -0
  39. package/dist/components/RewardCard.d.ts.map +1 -0
  40. package/dist/components/RewardCard.js +64 -0
  41. package/dist/components/RulesModal.d.ts +7 -0
  42. package/dist/components/RulesModal.d.ts.map +1 -0
  43. package/dist/components/RulesModal.js +106 -0
  44. package/dist/hooks/useBadges.d.ts +12 -0
  45. package/dist/hooks/useBadges.d.ts.map +1 -0
  46. package/dist/hooks/useBadges.js +42 -0
  47. package/dist/hooks/usePoints.d.ts +13 -0
  48. package/dist/hooks/usePoints.d.ts.map +1 -0
  49. package/dist/hooks/usePoints.js +70 -0
  50. package/dist/hooks/useQuests.d.ts +12 -0
  51. package/dist/hooks/useQuests.d.ts.map +1 -0
  52. package/dist/hooks/useQuests.js +41 -0
  53. package/dist/hooks/useQwikApp.d.ts +16 -0
  54. package/dist/hooks/useQwikApp.d.ts.map +1 -0
  55. package/dist/hooks/useQwikApp.js +42 -0
  56. package/dist/hooks/useRewards.d.ts +12 -0
  57. package/dist/hooks/useRewards.d.ts.map +1 -0
  58. package/dist/hooks/useRewards.js +56 -0
  59. package/dist/index.d.ts +27 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +29 -0
  62. package/dist/theme/context.d.ts +8 -0
  63. package/dist/theme/context.d.ts.map +1 -0
  64. package/dist/theme/context.js +8 -0
  65. package/dist/theme/tokens.d.ts +35 -0
  66. package/dist/theme/tokens.d.ts.map +1 -0
  67. package/dist/theme/tokens.js +33 -0
  68. package/dist/types/index.d.ts +119 -0
  69. package/dist/types/index.d.ts.map +1 -0
  70. package/dist/types/index.js +4 -0
  71. package/dist/web/widgetAssets.d.ts +4 -0
  72. package/dist/web/widgetAssets.d.ts.map +1 -0
  73. package/dist/web/widgetAssets.js +5 -0
  74. package/dist/web/widgetHtml.d.ts +2 -0
  75. package/dist/web/widgetHtml.d.ts.map +1 -0
  76. package/dist/web/widgetHtml.js +299 -0
  77. package/dist/web/widgetTypes.d.ts +128 -0
  78. package/dist/web/widgetTypes.d.ts.map +1 -0
  79. package/dist/web/widgetTypes.js +1 -0
  80. package/package.json +86 -0
  81. package/src/PlaybasisProvider.tsx +72 -0
  82. package/src/QwikCardApp.tsx +302 -0
  83. package/src/api/client.ts +307 -0
  84. package/src/components/Badge.tsx +51 -0
  85. package/src/components/BadgeIcon.tsx +97 -0
  86. package/src/components/Button.tsx +70 -0
  87. package/src/components/GradientCard.tsx +49 -0
  88. package/src/components/PointsBalance.tsx +122 -0
  89. package/src/components/ProgressBar.tsx +65 -0
  90. package/src/components/QuestProgress.tsx +153 -0
  91. package/src/components/RadialGauge.tsx +101 -0
  92. package/src/components/RewardCard.tsx +123 -0
  93. package/src/components/RulesModal.tsx +171 -0
  94. package/src/hooks/useBadges.ts +59 -0
  95. package/src/hooks/usePoints.ts +91 -0
  96. package/src/hooks/useQuests.ts +60 -0
  97. package/src/hooks/useQwikApp.ts +49 -0
  98. package/src/hooks/useRewards.ts +74 -0
  99. package/src/index.ts +34 -0
  100. package/src/theme/context.tsx +17 -0
  101. package/src/theme/tokens.ts +68 -0
  102. package/src/types/index.ts +176 -0
  103. package/src/web/widgetAssets.d.ts +3 -0
  104. package/src/web/widgetAssets.ts +6 -0
  105. package/src/web/widgetHtml.ts +302 -0
  106. package/src/web/widgetTypes.ts +146 -0
@@ -0,0 +1,302 @@
1
+ import { WIDGET_CSS, WIDGET_UMD_JS, WIDGET_SDK_VERSION } from './widgetAssets';
2
+
3
+ const escapeScript = (value: string) => value.replace(/<\/script/gi, '<\\/script');
4
+
5
+ export const getWidgetHtml = () => {
6
+ const umd = escapeScript(WIDGET_UMD_JS);
7
+ const css = WIDGET_CSS;
8
+
9
+ return `<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
14
+ <title>Playbasis Widget</title>
15
+ <style>
16
+ /* Base reset and responsive constraints */
17
+ *, *::before, *::after {
18
+ box-sizing: border-box;
19
+ }
20
+ html, body {
21
+ height: 100%;
22
+ width: 100%;
23
+ margin: 0;
24
+ padding: 0;
25
+ background: white;
26
+ overflow-x: hidden;
27
+ overflow-y: auto;
28
+ -webkit-overflow-scrolling: touch;
29
+ font-size: 14px;
30
+ }
31
+ /* Widget root container - CRITICAL for init() */
32
+ #pb-widget-root {
33
+ width: 100%;
34
+ min-height: 100vh;
35
+ max-width: 100vw;
36
+ overflow-x: hidden;
37
+ position: relative;
38
+ }
39
+ /* Playbasis widget container (created by init()) */
40
+ #playbasis-widget-container {
41
+ width: 100% !important;
42
+ max-width: 100vw !important;
43
+ height: auto !important;
44
+ }
45
+ /* Force responsive sizing on shadow DOM and children */
46
+ #playbasis-widget-container > * {
47
+ width: 100% !important;
48
+ max-width: 100vw !important;
49
+ }
50
+ /* Constrain SVG elements */
51
+ svg {
52
+ max-width: 100% !important;
53
+ height: auto !important;
54
+ }
55
+ /* Aggressive constraints on progress/orb elements */
56
+ [class*="progress"],
57
+ [class*="Progress"],
58
+ [class*="orb"],
59
+ [class*="Orb"],
60
+ [class*="radial"],
61
+ [class*="Radial"],
62
+ [class*="circular"],
63
+ [class*="Circular"] {
64
+ max-width: min(200px, 50vw) !important;
65
+ max-height: min(200px, 50vw) !important;
66
+ width: auto !important;
67
+ height: auto !important;
68
+ }
69
+ /* Dashboard and layout containers */
70
+ [class*="dashboard"],
71
+ [class*="Dashboard"],
72
+ [class*="container"],
73
+ [class*="Container"] {
74
+ width: 100% !important;
75
+ max-width: 100vw !important;
76
+ padding-left: 1rem !important;
77
+ padding-right: 1rem !important;
78
+ }
79
+ /* Responsive text scaling */
80
+ @media (max-width: 375px) {
81
+ html { font-size: 12px; }
82
+ [class*="text-"] { font-size: 0.875em !important; }
83
+ }
84
+ @media (min-width: 376px) and (max-width: 430px) {
85
+ html { font-size: 14px; }
86
+ }
87
+ @media (min-width: 431px) {
88
+ html { font-size: 16px; }
89
+ }
90
+ /* Mobile-specific overrides */
91
+ @media (max-width: 430px) {
92
+ [class*="w-"] {
93
+ max-width: 100vw !important;
94
+ }
95
+ [class*="h-64"],
96
+ [class*="h-72"],
97
+ [class*="h-80"],
98
+ [class*="h-96"] {
99
+ height: auto !important;
100
+ max-height: 50vh !important;
101
+ }
102
+ }
103
+ </style>
104
+ <style>${css}</style>
105
+ </head>
106
+ <body>
107
+ <!-- ERROR DISPLAY -->
108
+ <div id="error-display" style="display:none; padding:20px; font-family:sans-serif; color:#ef4444; text-align:center; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); max-width:90%; z-index:10000;">
109
+ <h3 style="margin-bottom:10px; font-size:16px;">Widget Error</h3>
110
+ <div id="error-message" style="font-size:11px; opacity:0.8; font-family:monospace; white-space:pre-wrap; word-break:break-word;"></div>
111
+ </div>
112
+
113
+ <!-- WIDGET MOUNT POINT (required by PlaybasisWidget.init) -->
114
+ <div id="pb-widget-root"></div>
115
+
116
+ <!-- POLYFILLS -->
117
+ <script>
118
+ // Fix 1: process.env.NODE_ENV polyfill
119
+ if (typeof window.process === 'undefined') {
120
+ window.process = { env: { NODE_ENV: 'production' } };
121
+ }
122
+
123
+ // Fix 2: module/exports polyfill for UMD compatibility
124
+ var __pbModuleExports = {};
125
+ if (typeof window.module === 'undefined') {
126
+ window.module = { exports: __pbModuleExports };
127
+ }
128
+ if (typeof window.exports === 'undefined') {
129
+ window.exports = __pbModuleExports;
130
+ }
131
+ </script>
132
+
133
+ <!-- WIDGET UMD BUNDLE -->
134
+ <script>
135
+ // Embedded @playbasis/widget-sdk UMD build (v${WIDGET_SDK_VERSION})
136
+ try {
137
+ ${umd}
138
+
139
+ // Fix 2 (cont): Re-attach to window if UMD exported to module.exports
140
+ if (!window.PlaybasisWidget && window.module && window.module.exports) {
141
+ if (window.module.exports.PlaybasisWidget) {
142
+ window.PlaybasisWidget = window.module.exports;
143
+ } else if (typeof window.module.exports === 'object' && window.module.exports.init) {
144
+ // Direct export case
145
+ window.PlaybasisWidget = window.module.exports;
146
+ }
147
+ }
148
+ } catch (e) {
149
+ console.error('Failed to load widget bundle:', e);
150
+ if (typeof showError === 'function') {
151
+ showError('Bundle Error: ' + e.message);
152
+ }
153
+ }
154
+ </script>
155
+
156
+ <!-- BOOTSTRAP BRIDGE -->
157
+ <script>
158
+ (function () {
159
+ function safeParse(text) {
160
+ try { return JSON.parse(text); } catch { return null; }
161
+ }
162
+
163
+ function postToNative(payload) {
164
+ if (!window.ReactNativeWebView && !window.ReactNativeWebView.postMessage) return;
165
+ window.ReactNativeWebView.postMessage(JSON.stringify(payload));
166
+ }
167
+
168
+ window.showError = function(msg) {
169
+ var el = document.getElementById('error-display');
170
+ var txt = document.getElementById('error-message');
171
+ if (el && txt) {
172
+ el.style.display = 'block';
173
+ txt.textContent = msg;
174
+ }
175
+ postToNative({ type: 'ERROR', error: msg });
176
+ };
177
+
178
+ window.onerror = function(msg, url, line, col, error) {
179
+ var errorStr = msg;
180
+ if (error && error.message) errorStr = error.message;
181
+ window.showError(errorStr);
182
+ return false;
183
+ };
184
+
185
+ var initConfig = null;
186
+ var bootstrapData = null;
187
+ var mounted = false;
188
+
189
+ // Native -> Web message handler
190
+ window.__PB_HANDLE_NATIVE_MESSAGE__ = function (payload) {
191
+ try {
192
+ if (!payload) return;
193
+ if (payload.type === 'RESPONSE') {
194
+ if (!payload.ok) {
195
+ console.error('[PlaybasisWidget] Native bootstrap error:', payload.error);
196
+ window.showError('Native Bootstrap Failed: ' + (payload.error || 'Unknown'));
197
+ return;
198
+ }
199
+ bootstrapData = payload.data || null;
200
+ tryMount();
201
+ }
202
+ } catch (e) {
203
+ window.showError('Message Handle Error: ' + e.message);
204
+ }
205
+ };
206
+
207
+ function tryMount() {
208
+ if (mounted || !initConfig || !bootstrapData) return;
209
+
210
+ // Verify container exists
211
+ var container = document.getElementById('pb-widget-root');
212
+ if (!container) {
213
+ window.showError('Container #pb-widget-root not found in DOM');
214
+ return;
215
+ }
216
+
217
+ // Locate widget module
218
+ var PlaybasisWidget = window.PlaybasisWidget;
219
+ if (!PlaybasisWidget) {
220
+ window.showError('PlaybasisWidget not found on window');
221
+ return;
222
+ }
223
+
224
+ // Find init function (could be PlaybasisWidget.init or PlaybasisWidget.PlaybasisWidget.init)
225
+ var initFn = null;
226
+ var targetObject = null;
227
+
228
+ if (typeof PlaybasisWidget.init === 'function') {
229
+ initFn = PlaybasisWidget.init;
230
+ targetObject = PlaybasisWidget;
231
+ } else if (PlaybasisWidget.PlaybasisWidget && typeof PlaybasisWidget.PlaybasisWidget.init === 'function') {
232
+ initFn = PlaybasisWidget.PlaybasisWidget.init;
233
+ targetObject = PlaybasisWidget.PlaybasisWidget;
234
+ }
235
+
236
+ if (!initFn) {
237
+ window.showError('PlaybasisWidget.init function not found');
238
+ return;
239
+ }
240
+
241
+ try {
242
+ mounted = true;
243
+ var tenantId = initConfig.config.tenantId;
244
+ var userId = initConfig.config.playerId;
245
+
246
+ // Call init with proper this context
247
+ initFn.call(targetObject, {
248
+ container: container,
249
+ embedded: true, // CRITICAL: Disable Shadow DOM so CSS applies correctly
250
+ apiKey: 'embedded',
251
+ tenantId: tenantId,
252
+ userId: userId,
253
+ themeName: 'qwik',
254
+ bootstrap: {
255
+ tenantId: tenantId,
256
+ ...bootstrapData,
257
+ },
258
+ });
259
+ } catch (e) {
260
+ window.showError('Mount Error: ' + e.message);
261
+ console.error('[PlaybasisWidget] Mount error details:', e);
262
+ }
263
+ }
264
+
265
+ function initIfReady() {
266
+ var init = window.__PB_INIT__;
267
+ if (!init || init.__applied) return;
268
+ init.__applied = true;
269
+ initConfig = init;
270
+
271
+ // Request bootstrap data from native
272
+ postToNative({
273
+ type: 'REQUEST',
274
+ requestId: String(Date.now()) + '-' + Math.random().toString(16).slice(2),
275
+ resource: 'bootstrap'
276
+ });
277
+ }
278
+
279
+ // Poll for injectedJavaScript
280
+ var start = Date.now();
281
+ var timer = setInterval(function () {
282
+ initIfReady();
283
+ if (Date.now() - start > 8000) {
284
+ clearInterval(timer);
285
+ if (!mounted && !bootstrapData) {
286
+ console.warn('[PlaybasisWidget] Bootstrap timed out');
287
+ }
288
+ }
289
+ }, 50);
290
+
291
+ // Listen for messages from native
292
+ function handleIncoming(event) {
293
+ var payload = typeof event.data === 'string' ? safeParse(event.data) : event.data;
294
+ if (payload) window.__PB_HANDLE_NATIVE_MESSAGE__(payload);
295
+ }
296
+ window.addEventListener('message', handleIncoming);
297
+ document.addEventListener('message', handleIncoming);
298
+ })();
299
+ </script>
300
+ </body>
301
+ </html>`;
302
+ };
@@ -0,0 +1,146 @@
1
+ export type WidgetBridgeResource = 'bootstrap';
2
+
3
+ export type WidgetBridgeMessage =
4
+ | {
5
+ type: 'INIT';
6
+ config: {
7
+ tenantId: string;
8
+ playerId: string;
9
+ baseUrl?: string;
10
+ };
11
+ platform?: string;
12
+ }
13
+ | {
14
+ type: 'REQUEST';
15
+ requestId: string;
16
+ resource: WidgetBridgeResource;
17
+ }
18
+ | {
19
+ type: 'RESPONSE';
20
+ requestId: string;
21
+ ok: true;
22
+ data: WidgetBootstrapData;
23
+ }
24
+ | {
25
+ type: 'RESPONSE';
26
+ requestId: string;
27
+ ok: false;
28
+ error: string;
29
+ }
30
+ | {
31
+ type: 'ERROR';
32
+ error: string;
33
+ }
34
+ | {
35
+ type: 'OPEN_GAME';
36
+ url: string;
37
+ title?: string;
38
+ };
39
+
40
+ export interface WidgetUserState {
41
+ userId: string;
42
+ level: number;
43
+ exp: number;
44
+ compositeScore: number;
45
+ positiveChangePct: number;
46
+ }
47
+
48
+ export interface WidgetTransaction {
49
+ id: string;
50
+ type: 'earn' | 'spend' | 'bonus';
51
+ amount: number;
52
+ description: string;
53
+ timestamp: number;
54
+ }
55
+
56
+ export interface WidgetCollectionItem {
57
+ id: string;
58
+ category: string;
59
+ rarity: string;
60
+ unlocked: boolean;
61
+ }
62
+
63
+ export interface WidgetNotification {
64
+ id: string;
65
+ type: 'badge' | 'level_up' | 'goal' | 'reward' | 'leaderboard';
66
+ message: string;
67
+ timestamp?: number;
68
+ read?: boolean;
69
+ }
70
+
71
+ export type WidgetGoalType = 'financial' | 'habit' | 'performance';
72
+ export type WidgetGoalStatus = 'on_track' | 'at_risk' | 'completed';
73
+
74
+ export interface WidgetGoal {
75
+ goalId: string;
76
+ type: WidgetGoalType;
77
+ target: number;
78
+ current: number;
79
+ progressPct: number;
80
+ startDate: string;
81
+ endDate: string;
82
+ status: WidgetGoalStatus;
83
+ rewards?: { xp?: number; badges?: string[] };
84
+ }
85
+
86
+ export interface WidgetActivityDay {
87
+ date: string;
88
+ count: number;
89
+ }
90
+
91
+ export interface WidgetLeaderboardEntry {
92
+ scope: 'global' | 'team' | 'season';
93
+ rank: number;
94
+ delta: number;
95
+ }
96
+
97
+ export type WidgetBadgeRarity = 'common' | 'rare' | 'epic' | 'legendary';
98
+
99
+ export interface WidgetBadge {
100
+ id: string;
101
+ name: string;
102
+ rarity: WidgetBadgeRarity;
103
+ unlocked: boolean;
104
+ imageUrl?: string;
105
+ }
106
+
107
+ export interface WidgetLeaderboardEntryDisplay {
108
+ userId?: string;
109
+ rank: number;
110
+ displayName: string;
111
+ score: number;
112
+ delta?: number;
113
+ isCurrentUser?: boolean;
114
+ avatarUrl?: string;
115
+ }
116
+
117
+ export interface WidgetWallet {
118
+ currency: string;
119
+ balance: number;
120
+ earnedDelta: number;
121
+ // Optional extension for QwikCard-specific display.
122
+ qwikCoins?: number;
123
+ }
124
+
125
+ export interface WidgetReward {
126
+ id: string;
127
+ name: string;
128
+ description: string;
129
+ cost: number;
130
+ imageUrl?: string;
131
+ }
132
+
133
+ export interface WidgetBootstrapData {
134
+ tenantId: string;
135
+ user: WidgetUserState;
136
+ wallet: WidgetWallet | null;
137
+ goals: WidgetGoal[];
138
+ activities: WidgetActivityDay[];
139
+ transactions?: WidgetTransaction[];
140
+ collections?: WidgetCollectionItem[];
141
+ badges: WidgetBadge[];
142
+ rewards?: WidgetReward[];
143
+ leaderboardEntries: WidgetLeaderboardEntryDisplay[];
144
+ leaderboard?: WidgetLeaderboardEntry | null;
145
+ notifications?: WidgetNotification[];
146
+ }