@cshah18/sdk 4.1.0 → 4.2.0

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.
@@ -1852,12 +1852,9 @@ class LobbyModal {
1852
1852
  // Top section
1853
1853
  const topSection = this.createTopSection();
1854
1854
  mainContent.appendChild(topSection);
1855
- // Activity section (hidden when offline redemption is present to keep layout compact)
1856
- const hasOfflineRedemption = this.data.offlineRedemption && isValidOfflineRedemption(this.data.offlineRedemption);
1857
- if (!hasOfflineRedemption) {
1858
- const activitySection = this.createActivitySection();
1859
- mainContent.appendChild(activitySection);
1860
- }
1855
+ // Activity section
1856
+ const activitySection = this.createActivitySection();
1857
+ mainContent.appendChild(activitySection);
1861
1858
  mainWrapper.appendChild(mainContent);
1862
1859
  modal.appendChild(background);
1863
1860
  modal.appendChild(mainWrapper);
@@ -1974,11 +1971,10 @@ class LobbyModal {
1974
1971
  connectedSection.appendChild(subtitle);
1975
1972
  // Check if group is fulfilled and has offline redemption
1976
1973
  const isComplete = !this.computeIsLocked(this.data);
1977
- const hasOfflineRedemption = isComplete &&
1974
+ if (isComplete &&
1978
1975
  this.data.offlineRedemption &&
1979
- isValidOfflineRedemption(this.data.offlineRedemption);
1980
- if (hasOfflineRedemption) {
1981
- // Show offline redemption view with integrated actions
1976
+ isValidOfflineRedemption(this.data.offlineRedemption)) {
1977
+ // Show offline redemption view instead of link/share
1982
1978
  const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
1983
1979
  connectedSection.appendChild(offlineSection);
1984
1980
  }
@@ -2034,9 +2030,6 @@ class LobbyModal {
2034
2030
  const section = document.createElement("div");
2035
2031
  section.className = "offline-redemption-section";
2036
2032
  section.id = "lobbyOfflineRedemptionSection";
2037
- // Top row: QR + Code side by side
2038
- const topRow = document.createElement("div");
2039
- topRow.className = "offline-top-row";
2040
2033
  // QR Code container
2041
2034
  const qrContainer = document.createElement("div");
2042
2035
  qrContainer.className = "offline-qr-container";
@@ -2066,40 +2059,29 @@ class LobbyModal {
2066
2059
  copyCodeBtn.className = "offline-copy-code-btn";
2067
2060
  copyCodeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>`;
2068
2061
  copyCodeBtn.addEventListener("click", () => this.copyOfflineRedemptionCode(offlineRedemption.redemption_code, copyCodeBtn));
2069
- const codeRow = document.createElement("div");
2070
- codeRow.className = "offline-code-row";
2071
- codeRow.appendChild(codeValue);
2072
- codeRow.appendChild(copyCodeBtn);
2073
2062
  codeBox.appendChild(codeLabel);
2074
- codeBox.appendChild(codeRow);
2075
- topRow.appendChild(qrContainer);
2076
- topRow.appendChild(codeBox);
2077
- // Expiry info - compact single line
2063
+ codeBox.appendChild(codeValue);
2064
+ codeBox.appendChild(copyCodeBtn);
2065
+ // Expiry info
2078
2066
  const expiryInfo = document.createElement("div");
2079
2067
  expiryInfo.className = "offline-expiry-info";
2080
- expiryInfo.innerHTML = `<span class="offline-expiry-label">Valid Until:</span> <span class="offline-expiry-value">${formatExpiryDate(offlineRedemption.offline_expires_at)}</span>`;
2081
- // Action buttons row
2082
- const actionsRow = document.createElement("div");
2083
- actionsRow.className = "offline-actions-row";
2068
+ const expiryLabel = document.createElement("p");
2069
+ expiryLabel.className = "offline-expiry-label";
2070
+ expiryLabel.textContent = "Valid Until";
2071
+ const expiryValue = document.createElement("p");
2072
+ expiryValue.className = "offline-expiry-value";
2073
+ expiryValue.textContent = formatExpiryDate(offlineRedemption.offline_expires_at);
2074
+ expiryInfo.appendChild(expiryLabel);
2075
+ expiryInfo.appendChild(expiryValue);
2084
2076
  // Download QR button
2085
2077
  const downloadQRBtn = document.createElement("button");
2086
2078
  downloadQRBtn.className = "offline-download-qr-btn";
2087
2079
  downloadQRBtn.textContent = "Download QR";
2088
2080
  downloadQRBtn.addEventListener("click", () => this.downloadOfflineQR(offlineRedemption, downloadQRBtn));
2089
- // Online checkout button
2090
- const onlineCheckoutBtn = document.createElement("button");
2091
- onlineCheckoutBtn.className = "offline-online-checkout-btn";
2092
- onlineCheckoutBtn.textContent = "Checkout Online";
2093
- onlineCheckoutBtn.addEventListener("click", () => {
2094
- if (this.data.groupLink) {
2095
- window.open(this.data.groupLink, "_blank");
2096
- }
2097
- });
2098
- actionsRow.appendChild(downloadQRBtn);
2099
- actionsRow.appendChild(onlineCheckoutBtn);
2100
- section.appendChild(topRow);
2081
+ section.appendChild(qrContainer);
2082
+ section.appendChild(codeBox);
2101
2083
  section.appendChild(expiryInfo);
2102
- section.appendChild(actionsRow);
2084
+ section.appendChild(downloadQRBtn);
2103
2085
  // Inject styles for offline redemption section
2104
2086
  this.injectOfflineRedemptionStyles();
2105
2087
  return section;
@@ -2144,184 +2126,117 @@ class LobbyModal {
2144
2126
  .offline-redemption-section {
2145
2127
  display: flex;
2146
2128
  flex-direction: column;
2147
- gap: 14px;
2148
- padding: 16px;
2149
- background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
2150
- border: 1.5px solid #e5e7eb;
2151
- border-radius: 12px;
2152
- margin-top: 12px;
2153
- }
2154
-
2155
- .offline-top-row {
2156
- display: flex;
2157
- gap: 14px;
2158
- align-items: stretch;
2129
+ gap: 16px;
2130
+ padding: 16px 0;
2159
2131
  }
2160
2132
 
2161
2133
  .offline-qr-container {
2162
- flex: 0 0 120px;
2163
2134
  display: flex;
2164
2135
  justify-content: center;
2165
- align-items: center;
2166
- background: white;
2167
- padding: 10px;
2136
+ background: #f9fafb;
2137
+ padding: 12px;
2168
2138
  border-radius: 8px;
2169
- border: 1.5px solid #e5e7eb;
2170
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
2139
+ min-height: 160px;
2140
+ align-items: center;
2171
2141
  }
2172
2142
 
2173
2143
  .offline-qr-image {
2174
- max-width: 100px;
2175
- max-height: 100px;
2144
+ max-width: 140px;
2145
+ max-height: 140px;
2176
2146
  width: auto;
2177
2147
  height: auto;
2178
- filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.08));
2179
2148
  }
2180
2149
 
2181
2150
  .offline-qr-fallback {
2182
2151
  color: #9ca3af;
2183
- font-size: 11px;
2152
+ font-size: 12px;
2184
2153
  text-align: center;
2185
2154
  }
2186
2155
 
2187
2156
  .offline-code-box {
2188
- flex: 1;
2189
2157
  display: flex;
2190
2158
  flex-direction: column;
2191
- gap: 8px;
2192
- justify-content: center;
2193
- }
2194
-
2195
- .offline-code-row {
2196
- position: relative;
2197
- display: flex;
2198
- align-items: center;
2159
+ gap: 6px;
2199
2160
  }
2200
2161
 
2201
2162
  .offline-code-label {
2202
2163
  margin: 0;
2203
2164
  font-size: 11px;
2204
- font-weight: 700;
2205
- color: #1f2937;
2165
+ font-weight: 600;
2166
+ color: #6b7280;
2206
2167
  text-transform: uppercase;
2207
2168
  letter-spacing: 0.5px;
2208
2169
  }
2209
2170
 
2210
2171
  .offline-code-value {
2211
- font-family: 'Courier New', monospace;
2172
+ font-family: monospace;
2212
2173
  font-size: 14px;
2213
- font-weight: 700;
2174
+ font-weight: 600;
2214
2175
  color: #111827;
2215
- padding: 10px 40px 10px 12px;
2216
- background: white;
2217
- border: 1.5px solid #d1d5db;
2218
- border-radius: 8px;
2176
+ padding: 10px 12px;
2177
+ background: #f9fafb;
2178
+ border: 1px solid #e5e7eb;
2179
+ border-radius: 6px;
2219
2180
  text-align: center;
2220
- letter-spacing: 1.5px;
2181
+ letter-spacing: 1px;
2221
2182
  position: relative;
2222
2183
  display: flex;
2223
2184
  align-items: center;
2224
2185
  justify-content: center;
2225
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
2226
- transition: all 0.2s;
2227
- }
2228
-
2229
- .offline-code-row .offline-code-value {
2230
- width: 100%;
2231
- }
2232
-
2233
- .offline-code-value:hover {
2234
- border-color: #3b82f6;
2235
- box-shadow: 0 2px 6px rgba(59, 130, 246, 0.1);
2236
2186
  }
2237
2187
 
2238
2188
  .offline-copy-code-btn {
2239
2189
  position: absolute;
2240
- right: 6px;
2241
- top: 50%;
2242
- transform: translateY(-50%);
2190
+ right: 8px;
2243
2191
  background: none;
2244
2192
  border: none;
2245
- padding: 5px 8px;
2193
+ padding: 4px 8px;
2246
2194
  cursor: pointer;
2247
2195
  color: #6b7280;
2248
2196
  display: flex;
2249
2197
  align-items: center;
2250
- justify-content: center;
2251
- transition: all 0.2s;
2252
- border-radius: 4px;
2198
+ transition: color 0.2s;
2253
2199
  }
2254
2200
 
2255
2201
  .offline-copy-code-btn:hover {
2256
- color: #3b82f6;
2257
- background: #eff6ff;
2202
+ color: #111827;
2258
2203
  }
2259
2204
 
2260
2205
  .offline-expiry-info {
2261
- font-size: 11px;
2262
- color: #6b7280;
2263
- text-align: center;
2264
- padding: 6px 0;
2206
+ display: flex;
2207
+ flex-direction: column;
2208
+ gap: 4px;
2265
2209
  }
2266
2210
 
2267
2211
  .offline-expiry-label {
2268
- font-weight: 700;
2212
+ margin: 0;
2213
+ font-size: 11px;
2214
+ font-weight: 600;
2215
+ color: #6b7280;
2269
2216
  text-transform: uppercase;
2270
2217
  letter-spacing: 0.5px;
2271
- color: #1f2937;
2272
2218
  }
2273
2219
 
2274
2220
  .offline-expiry-value {
2275
- font-weight: 600;
2221
+ margin: 0;
2222
+ font-size: 13px;
2276
2223
  color: #374151;
2277
2224
  }
2278
2225
 
2279
- .offline-actions-row {
2280
- display: flex;
2281
- gap: 10px;
2282
- }
2283
-
2284
- .offline-download-qr-btn,
2285
- .offline-online-checkout-btn {
2286
- flex: 1;
2287
- padding: 11px 16px;
2226
+ .offline-download-qr-btn {
2227
+ padding: 10px 16px;
2228
+ background: #3b82f6;
2229
+ color: white;
2288
2230
  border: none;
2289
- border-radius: 8px;
2231
+ border-radius: 6px;
2290
2232
  font-size: 13px;
2291
- font-weight: 700;
2233
+ font-weight: 600;
2292
2234
  cursor: pointer;
2293
- transition: all 0.2s;
2294
- text-transform: uppercase;
2295
- letter-spacing: 0.4px;
2296
- }
2297
-
2298
- .offline-download-qr-btn {
2299
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
2300
- color: white;
2301
- box-shadow: 0 2px 6px rgba(59, 130, 246, 0.2);
2235
+ transition: background 0.2s;
2302
2236
  }
2303
2237
 
2304
2238
  .offline-download-qr-btn:hover:not(:disabled) {
2305
- background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
2306
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
2307
- transform: translateY(-1px);
2308
- }
2309
-
2310
- .offline-online-checkout-btn {
2311
- background: linear-gradient(135deg, #111827 0%, #1f2937 100%);
2312
- color: white;
2313
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
2314
- }
2315
-
2316
- .offline-online-checkout-btn:hover {
2317
- background: linear-gradient(135deg, #1f2937 0%, #374151 100%);
2318
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
2319
- transform: translateY(-1px);
2320
- }
2321
-
2322
- .offline-download-qr-btn:active:not(:disabled),
2323
- .offline-online-checkout-btn:active {
2324
- transform: translateY(0);
2239
+ background: #2563eb;
2325
2240
  }
2326
2241
 
2327
2242
  .offline-download-qr-btn:disabled {
@@ -5622,12 +5537,28 @@ class ApiClient {
5622
5537
  * @param checkoutRef - The checkout reference ID from the prepare step
5623
5538
  * @returns Promise with success status
5624
5539
  */
5625
- async confirmCheckout(groupId, checkoutRef) {
5540
+ async confirmCheckout(groupId, checkoutRef, data) {
5626
5541
  const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_CHECKOUT_CONFIRM, { groupId });
5627
5542
  this.logger.info(`Confirming checkout for group: ${groupId}`);
5628
- const response = await this.post(endpoint, {
5543
+ const payload = {
5629
5544
  checkout_ref: checkoutRef,
5630
- });
5545
+ };
5546
+ // Add optional fields if provided
5547
+ if (data) {
5548
+ if (data.order_id)
5549
+ payload.order_id = data.order_id;
5550
+ if (data.order_total !== undefined)
5551
+ payload.order_total = data.order_total;
5552
+ if (data.discount_applied !== undefined)
5553
+ payload.discount_applied = data.discount_applied;
5554
+ if (data.currency)
5555
+ payload.currency = data.currency;
5556
+ if (data.payment_method)
5557
+ payload.payment_method = data.payment_method;
5558
+ if (data.metadata)
5559
+ payload.metadata = data.metadata;
5560
+ }
5561
+ const response = await this.post(endpoint, payload);
5631
5562
  if (response.success) {
5632
5563
  this.logger.info("Checkout confirmed successfully");
5633
5564
  return {
@@ -10531,14 +10462,31 @@ class CoBuy {
10531
10462
  *
10532
10463
  * @param groupId - The group ID to confirm checkout for
10533
10464
  * @param checkoutRef - The checkout reference ID from the prepare step
10465
+ /**
10466
+ * Confirm checkout for a group
10467
+ * Should be called after validateCheckout to complete the checkout process.
10468
+ *
10469
+ * @param groupId The group ID to confirm checkout for
10470
+ * @param checkoutRef The checkout reference from the prepare step
10471
+ * @param data Optional order details (order_id, order_total, discount_applied, currency, payment_method, metadata)
10534
10472
  *
10535
10473
  * @example
10536
10474
  * ```typescript
10537
10475
  * // Confirm checkout for a group
10538
10476
  * await CoBuy.confirmCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509');
10477
+ *
10478
+ * // Confirm checkout with order details
10479
+ * await CoBuy.confirmCheckout('fae238ae-7468-47e9-9eec-b6d52fe3b012', 'chk_9a6d8750-ed60-4795-a207-2abe955e8509', {
10480
+ * order_id: 'ORDER-1001',
10481
+ * order_total: 100.00,
10482
+ * discount_applied: 20.00,
10483
+ * currency: 'USD',
10484
+ * payment_method: 'card',
10485
+ * metadata: { coupon_code: 'WELCOME20', customer_id: 'cust_123' }
10486
+ * });
10539
10487
  * ```
10540
10488
  */
10541
- async confirmCheckout(groupId, checkoutRef) {
10489
+ async confirmCheckout(groupId, checkoutRef, data) {
10542
10490
  if (!this.configManager.isInitialized()) {
10543
10491
  this.logger.warn("SDK not initialized, cannot confirm checkout");
10544
10492
  return;
@@ -10548,7 +10496,7 @@ class CoBuy {
10548
10496
  return;
10549
10497
  }
10550
10498
  try {
10551
- const response = await this.apiClient.confirmCheckout(groupId, checkoutRef);
10499
+ const response = await this.apiClient.confirmCheckout(groupId, checkoutRef, data);
10552
10500
  if (response.success) {
10553
10501
  this.logger.info(`Checkout confirmed successfully for group: ${groupId}`);
10554
10502
  // Directly refresh widgets. If we can infer productId, refresh only matching widgets.