@cshah18/sdk 4.2.0 → 4.4.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,9 +1852,12 @@ class LobbyModal {
1852
1852
  // Top section
1853
1853
  const topSection = this.createTopSection();
1854
1854
  mainContent.appendChild(topSection);
1855
- // Activity section
1856
- const activitySection = this.createActivitySection();
1857
- mainContent.appendChild(activitySection);
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
+ }
1858
1861
  mainWrapper.appendChild(mainContent);
1859
1862
  modal.appendChild(background);
1860
1863
  modal.appendChild(mainWrapper);
@@ -1971,10 +1974,11 @@ class LobbyModal {
1971
1974
  connectedSection.appendChild(subtitle);
1972
1975
  // Check if group is fulfilled and has offline redemption
1973
1976
  const isComplete = !this.computeIsLocked(this.data);
1974
- if (isComplete &&
1977
+ const hasOfflineRedemption = isComplete &&
1975
1978
  this.data.offlineRedemption &&
1976
- isValidOfflineRedemption(this.data.offlineRedemption)) {
1977
- // Show offline redemption view instead of link/share
1979
+ isValidOfflineRedemption(this.data.offlineRedemption);
1980
+ if (hasOfflineRedemption) {
1981
+ // Show offline redemption view with integrated actions
1978
1982
  const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
1979
1983
  connectedSection.appendChild(offlineSection);
1980
1984
  }
@@ -2030,6 +2034,9 @@ class LobbyModal {
2030
2034
  const section = document.createElement("div");
2031
2035
  section.className = "offline-redemption-section";
2032
2036
  section.id = "lobbyOfflineRedemptionSection";
2037
+ // Top row: QR + Code side by side
2038
+ const topRow = document.createElement("div");
2039
+ topRow.className = "offline-top-row";
2033
2040
  // QR Code container
2034
2041
  const qrContainer = document.createElement("div");
2035
2042
  qrContainer.className = "offline-qr-container";
@@ -2059,29 +2066,42 @@ class LobbyModal {
2059
2066
  copyCodeBtn.className = "offline-copy-code-btn";
2060
2067
  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>`;
2061
2068
  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);
2062
2073
  codeBox.appendChild(codeLabel);
2063
- codeBox.appendChild(codeValue);
2064
- codeBox.appendChild(copyCodeBtn);
2065
- // Expiry info
2074
+ codeBox.appendChild(codeRow);
2075
+ topRow.appendChild(qrContainer);
2076
+ topRow.appendChild(codeBox);
2077
+ // Expiry info - compact single line
2066
2078
  const expiryInfo = document.createElement("div");
2067
2079
  expiryInfo.className = "offline-expiry-info";
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);
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";
2076
2084
  // Download QR button
2077
2085
  const downloadQRBtn = document.createElement("button");
2078
2086
  downloadQRBtn.className = "offline-download-qr-btn";
2079
2087
  downloadQRBtn.textContent = "Download QR";
2080
2088
  downloadQRBtn.addEventListener("click", () => this.downloadOfflineQR(offlineRedemption, downloadQRBtn));
2081
- section.appendChild(qrContainer);
2082
- section.appendChild(codeBox);
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
+ this.close();
2095
+ // Ensure modal closes before triggering checkout intent
2096
+ window.setTimeout(() => {
2097
+ this.triggerContinueToCheckout();
2098
+ }, 0);
2099
+ });
2100
+ actionsRow.appendChild(downloadQRBtn);
2101
+ actionsRow.appendChild(onlineCheckoutBtn);
2102
+ section.appendChild(topRow);
2083
2103
  section.appendChild(expiryInfo);
2084
- section.appendChild(downloadQRBtn);
2104
+ section.appendChild(actionsRow);
2085
2105
  // Inject styles for offline redemption section
2086
2106
  this.injectOfflineRedemptionStyles();
2087
2107
  return section;
@@ -2126,117 +2146,185 @@ class LobbyModal {
2126
2146
  .offline-redemption-section {
2127
2147
  display: flex;
2128
2148
  flex-direction: column;
2129
- gap: 16px;
2130
- padding: 16px 0;
2149
+ gap: 14px;
2150
+ padding: 16px;
2151
+ background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
2152
+ border: 1.5px solid #e5e7eb;
2153
+ border-radius: 12px;
2154
+ margin-top: 12px;
2155
+ }
2156
+
2157
+ .offline-top-row {
2158
+ display: flex;
2159
+ gap: 14px;
2160
+ align-items: stretch;
2131
2161
  }
2132
2162
 
2133
2163
  .offline-qr-container {
2164
+ flex: 0 0 120px;
2134
2165
  display: flex;
2135
2166
  justify-content: center;
2136
- background: #f9fafb;
2137
- padding: 12px;
2138
- border-radius: 8px;
2139
- min-height: 160px;
2140
2167
  align-items: center;
2168
+ background: white;
2169
+ padding: 10px;
2170
+ border-radius: 8px;
2171
+ border: 1.5px solid #e5e7eb;
2172
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
2141
2173
  }
2142
2174
 
2143
2175
  .offline-qr-image {
2144
- max-width: 140px;
2145
- max-height: 140px;
2176
+ max-width: 100px;
2177
+ max-height: 100px;
2146
2178
  width: auto;
2147
2179
  height: auto;
2180
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.08));
2148
2181
  }
2149
2182
 
2150
2183
  .offline-qr-fallback {
2151
2184
  color: #9ca3af;
2152
- font-size: 12px;
2185
+ font-size: 11px;
2153
2186
  text-align: center;
2154
2187
  }
2155
2188
 
2156
2189
  .offline-code-box {
2190
+ flex: 1;
2157
2191
  display: flex;
2158
2192
  flex-direction: column;
2159
- gap: 6px;
2193
+ gap: 8px;
2194
+ justify-content: center;
2195
+ }
2196
+
2197
+ .offline-code-row {
2198
+ position: relative;
2199
+ display: flex;
2200
+ align-items: center;
2160
2201
  }
2161
2202
 
2162
2203
  .offline-code-label {
2163
2204
  margin: 0;
2164
2205
  font-size: 11px;
2165
- font-weight: 600;
2166
- color: #6b7280;
2206
+ font-weight: 700;
2207
+ color: #1f2937;
2167
2208
  text-transform: uppercase;
2168
2209
  letter-spacing: 0.5px;
2169
2210
  }
2170
2211
 
2171
2212
  .offline-code-value {
2172
- font-family: monospace;
2213
+ font-family: 'Courier New', monospace;
2173
2214
  font-size: 14px;
2174
- font-weight: 600;
2215
+ font-weight: 700;
2175
2216
  color: #111827;
2176
- padding: 10px 12px;
2177
- background: #f9fafb;
2178
- border: 1px solid #e5e7eb;
2179
- border-radius: 6px;
2217
+ padding: 10px 40px 10px 12px;
2218
+ background: white;
2219
+ border: 1.5px solid #d1d5db;
2220
+ border-radius: 8px;
2180
2221
  text-align: center;
2181
- letter-spacing: 1px;
2222
+ letter-spacing: 1.5px;
2182
2223
  position: relative;
2183
2224
  display: flex;
2184
2225
  align-items: center;
2185
2226
  justify-content: center;
2227
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
2228
+ transition: all 0.2s;
2229
+ word-break: break-all;
2230
+ }
2231
+
2232
+ .offline-code-row .offline-code-value {
2233
+ width: 100%;
2234
+ }
2235
+
2236
+ .offline-code-value:hover {
2237
+ border-color: #3b82f6;
2238
+ box-shadow: 0 2px 6px rgba(59, 130, 246, 0.1);
2186
2239
  }
2187
2240
 
2188
2241
  .offline-copy-code-btn {
2189
2242
  position: absolute;
2190
- right: 8px;
2243
+ right: 6px;
2244
+ top: 50%;
2245
+ transform: translateY(-50%);
2191
2246
  background: none;
2192
2247
  border: none;
2193
- padding: 4px 8px;
2248
+ padding: 5px 8px;
2194
2249
  cursor: pointer;
2195
2250
  color: #6b7280;
2196
2251
  display: flex;
2197
2252
  align-items: center;
2198
- transition: color 0.2s;
2253
+ justify-content: center;
2254
+ transition: all 0.2s;
2255
+ border-radius: 4px;
2199
2256
  }
2200
2257
 
2201
2258
  .offline-copy-code-btn:hover {
2202
- color: #111827;
2259
+ color: #3b82f6;
2260
+ background: #eff6ff;
2203
2261
  }
2204
2262
 
2205
2263
  .offline-expiry-info {
2206
- display: flex;
2207
- flex-direction: column;
2208
- gap: 4px;
2264
+ font-size: 11px;
2265
+ color: #6b7280;
2266
+ text-align: center;
2267
+ padding: 6px 0;
2209
2268
  }
2210
2269
 
2211
2270
  .offline-expiry-label {
2212
- margin: 0;
2213
- font-size: 11px;
2214
- font-weight: 600;
2215
- color: #6b7280;
2271
+ font-weight: 700;
2216
2272
  text-transform: uppercase;
2217
2273
  letter-spacing: 0.5px;
2274
+ color: #1f2937;
2218
2275
  }
2219
2276
 
2220
2277
  .offline-expiry-value {
2221
- margin: 0;
2222
- font-size: 13px;
2278
+ font-weight: 600;
2223
2279
  color: #374151;
2224
2280
  }
2225
2281
 
2226
- .offline-download-qr-btn {
2227
- padding: 10px 16px;
2228
- background: #3b82f6;
2229
- color: white;
2282
+ .offline-actions-row {
2283
+ display: flex;
2284
+ gap: 10px;
2285
+ }
2286
+
2287
+ .offline-download-qr-btn,
2288
+ .offline-online-checkout-btn {
2289
+ flex: 1;
2290
+ padding: 11px 16px;
2230
2291
  border: none;
2231
- border-radius: 6px;
2292
+ border-radius: 8px;
2232
2293
  font-size: 13px;
2233
- font-weight: 600;
2294
+ font-weight: 700;
2234
2295
  cursor: pointer;
2235
- transition: background 0.2s;
2296
+ transition: all 0.2s;
2297
+ text-transform: uppercase;
2298
+ letter-spacing: 0.4px;
2299
+ }
2300
+
2301
+ .offline-download-qr-btn {
2302
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
2303
+ color: white;
2304
+ box-shadow: 0 2px 6px rgba(59, 130, 246, 0.2);
2236
2305
  }
2237
2306
 
2238
2307
  .offline-download-qr-btn:hover:not(:disabled) {
2239
- background: #2563eb;
2308
+ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
2309
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
2310
+ transform: translateY(-1px);
2311
+ }
2312
+
2313
+ .offline-online-checkout-btn {
2314
+ background: linear-gradient(135deg, #111827 0%, #1f2937 100%);
2315
+ color: white;
2316
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
2317
+ }
2318
+
2319
+ .offline-online-checkout-btn:hover {
2320
+ background: linear-gradient(135deg, #1f2937 0%, #374151 100%);
2321
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
2322
+ transform: translateY(-1px);
2323
+ }
2324
+
2325
+ .offline-download-qr-btn:active:not(:disabled),
2326
+ .offline-online-checkout-btn:active {
2327
+ transform: translateY(0);
2240
2328
  }
2241
2329
 
2242
2330
  .offline-download-qr-btn:disabled {
@@ -2246,6 +2334,21 @@ class LobbyModal {
2246
2334
  `;
2247
2335
  document.head.appendChild(style);
2248
2336
  }
2337
+ /**
2338
+ * Trigger the same checkout flow as clicking "Continue to Checkout" on the widget
2339
+ */
2340
+ triggerContinueToCheckout() {
2341
+ const buttons = Array.from(document.querySelectorAll(".cobuy-button"));
2342
+ const target = buttons.find((btn) => {
2343
+ const text = (btn.textContent || "").trim().toLowerCase();
2344
+ return text.includes("continue to checkout") || text === "checkout" || text.includes("checkout");
2345
+ });
2346
+ if (!target) {
2347
+ this.logger.warn("[LobbyModal] Could not find a checkout button to trigger");
2348
+ return;
2349
+ }
2350
+ target.click();
2351
+ }
2249
2352
  /**
2250
2353
  * Create offer section
2251
2354
  */