@cshah18/sdk 4.0.0 → 4.1.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.
@@ -507,12 +507,14 @@ var CoBuySDK = (function (exports) {
507
507
  // Convert API groups to display format
508
508
  const groups = response.data.groups.map((apiGroup) => ({
509
509
  groupId: apiGroup.id,
510
+ name: apiGroup.group_name,
510
511
  timeLabel: this.formatTimeRemaining(apiGroup.timeLeftSeconds),
511
512
  timeLeftSeconds: apiGroup.timeLeftSeconds,
512
513
  joined: apiGroup.participants_count,
513
514
  total: apiGroup.max_participants,
514
515
  isMember: Boolean(apiGroup.is_member),
515
516
  }));
517
+ console.log("groupsss", groups);
516
518
  // If API reports membership, prefer it over cached state
517
519
  const memberGroup = groups.find((g) => g.isMember);
518
520
  if (memberGroup) {
@@ -819,12 +821,12 @@ var CoBuySDK = (function (exports) {
819
821
  createGroupCard(group) {
820
822
  const card = document.createElement("div");
821
823
  card.className = "cobuy-group-card";
822
- card.setAttribute("data-group-id", group.groupId);
824
+ card.setAttribute("data-group-id", group.name || group.groupId);
823
825
  const header = document.createElement("div");
824
826
  header.className = "cobuy-group-card-header";
825
827
  const groupId = document.createElement("div");
826
828
  groupId.className = "cobuy-group-id";
827
- groupId.textContent = `Group #${group.groupId}`;
829
+ groupId.textContent = `Group #${group.name || group.groupId}`;
828
830
  const timer = document.createElement("div");
829
831
  timer.className = "cobuy-group-timer";
830
832
  timer.innerHTML = `
@@ -1363,6 +1365,158 @@ var CoBuySDK = (function (exports) {
1363
1365
  }
1364
1366
  }
1365
1367
 
1368
+ /**
1369
+ * Offline Redemption Utilities
1370
+ * Helpers for constructing QR URLs, copying codes, and downloading QR images
1371
+ */
1372
+ const S3_BUCKET = "cobuy-dev";
1373
+ const S3_REGION = "af-south-1";
1374
+ /**
1375
+ * Build full S3 URL for QR code image
1376
+ * @param qrCodeData - S3 key from offline_redemption.qr_code_data
1377
+ * @returns Full HTTPS URL to the QR code image
1378
+ */
1379
+ function buildQRUrl(qrCodeData) {
1380
+ if (!qrCodeData) {
1381
+ return "";
1382
+ }
1383
+ const prefix = `https://${S3_BUCKET}.s3.${S3_REGION}.amazonaws.com/`;
1384
+ return `${prefix}${qrCodeData}`;
1385
+ }
1386
+ /**
1387
+ * Copy redemption code to clipboard
1388
+ * @param code - Redemption code to copy
1389
+ * @returns Promise that resolves when copy is complete
1390
+ */
1391
+ async function copyRedemptionCode(code) {
1392
+ const logger = new Logger(false);
1393
+ if (!code) {
1394
+ logger.warn("Cannot copy empty redemption code");
1395
+ return false;
1396
+ }
1397
+ try {
1398
+ // Use modern clipboard API if available
1399
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1400
+ await navigator.clipboard.writeText(code);
1401
+ logger.info("Redemption code copied to clipboard");
1402
+ return true;
1403
+ }
1404
+ // Fallback for older browsers
1405
+ const textarea = document.createElement("textarea");
1406
+ textarea.value = code;
1407
+ textarea.style.position = "fixed";
1408
+ textarea.style.opacity = "0";
1409
+ document.body.appendChild(textarea);
1410
+ textarea.select();
1411
+ textarea.setSelectionRange(0, 99999); // For mobile devices
1412
+ const successful = document.execCommand("copy");
1413
+ document.body.removeChild(textarea);
1414
+ if (successful) {
1415
+ logger.info("Redemption code copied to clipboard (fallback method)");
1416
+ return true;
1417
+ }
1418
+ return false;
1419
+ }
1420
+ catch (error) {
1421
+ logger.error("Failed to copy redemption code", error);
1422
+ return false;
1423
+ }
1424
+ }
1425
+ /**
1426
+ * Download QR code image
1427
+ * @param qrUrl - Full URL to the QR code image
1428
+ * @param filename - Name for the downloaded file
1429
+ */
1430
+ async function downloadQRCode(qrUrl, filename = "redemption-qr.png") {
1431
+ const logger = new Logger(false);
1432
+ if (!qrUrl) {
1433
+ logger.warn("Cannot download QR code - empty URL");
1434
+ return false;
1435
+ }
1436
+ try {
1437
+ const response = await fetch(qrUrl);
1438
+ if (!response.ok) {
1439
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1440
+ }
1441
+ const blob = await response.blob();
1442
+ const blobUrl = window.URL.createObjectURL(blob);
1443
+ const link = document.createElement("a");
1444
+ link.href = blobUrl;
1445
+ link.download = filename;
1446
+ link.style.display = "none";
1447
+ document.body.appendChild(link);
1448
+ link.click();
1449
+ document.body.removeChild(link);
1450
+ window.URL.revokeObjectURL(blobUrl);
1451
+ logger.info("QR code downloaded successfully");
1452
+ return true;
1453
+ }
1454
+ catch (error) {
1455
+ logger.error("Failed to download QR code", error);
1456
+ return false;
1457
+ }
1458
+ }
1459
+ /**
1460
+ * Validate offline redemption data
1461
+ * @param redemption - Offline redemption object
1462
+ * @returns true if data is valid and complete
1463
+ */
1464
+ function isValidOfflineRedemption(redemption) {
1465
+ if (!redemption || typeof redemption !== "object") {
1466
+ return false;
1467
+ }
1468
+ const obj = redemption;
1469
+ return (typeof obj.member_id === "string" &&
1470
+ obj.member_id.length > 0 &&
1471
+ typeof obj.qr_code_value === "string" &&
1472
+ obj.qr_code_value.length > 0 &&
1473
+ typeof obj.redemption_code === "string" &&
1474
+ obj.redemption_code.length > 0 &&
1475
+ typeof obj.qr_code_data === "string" &&
1476
+ obj.qr_code_data.length > 0 &&
1477
+ typeof obj.offline_expires_at === "string" &&
1478
+ obj.offline_expires_at.length > 0);
1479
+ }
1480
+ /**
1481
+ * Format expiry date for display
1482
+ * @param isoDate - ISO 8601 date string
1483
+ * @returns Formatted date string (e.g., "Feb 20, 2026 at 10:21 AM")
1484
+ */
1485
+ function formatExpiryDate(isoDate) {
1486
+ try {
1487
+ const date = new Date(isoDate);
1488
+ // Check if date is valid
1489
+ if (isNaN(date.getTime())) {
1490
+ return "Invalid date";
1491
+ }
1492
+ return date.toLocaleDateString("en-US", {
1493
+ year: "numeric",
1494
+ month: "short",
1495
+ day: "numeric",
1496
+ hour: "2-digit",
1497
+ minute: "2-digit",
1498
+ });
1499
+ }
1500
+ catch (_a) {
1501
+ return "Invalid date";
1502
+ }
1503
+ }
1504
+ /**
1505
+ * Check if offline redemption is still valid
1506
+ * @param expiryDate - ISO 8601 expiry date string
1507
+ * @returns true if redemption has not expired
1508
+ */
1509
+ function isRedemptionValid(expiryDate) {
1510
+ try {
1511
+ const expiry = new Date(expiryDate);
1512
+ const now = new Date();
1513
+ return now < expiry;
1514
+ }
1515
+ catch (_a) {
1516
+ return false;
1517
+ }
1518
+ }
1519
+
1366
1520
  function styleInject(css, ref) {
1367
1521
  if ( ref === void 0 ) ref = {};
1368
1522
  var insertAt = ref.insertAt;
@@ -1390,7 +1544,7 @@ var CoBuySDK = (function (exports) {
1390
1544
  }
1391
1545
  }
1392
1546
 
1393
- var css_248z = ".cb-lobby-modal-container{height:100%;left:0;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:green;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{font-size:20px;line-height:1;margin-bottom:10px}.lobby-link-box{align-items:center;backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:1rem;box-shadow:0 1px 3px 0 #0000001a;display:flex;flex:1;gap:20px;justify-content:space-between;padding:13px 20px}.lobby-link-text{font-size:12px;font-weight:600;line-height:1;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;color:#1d293d;cursor:pointer;display:flex;justify-content:center;padding:5px}.lobby-link-url{font-size:16px;font-weight:700;line-height:1}.lobby-link-wp{align-items:center;display:flex;gap:20px;justify-content:space-between;margin-top:40px}.share-btn-wp .share-btn{align-items:center;background:#000;border-radius:15px;color:#fff;cursor:pointer;display:flex;height:68px;justify-content:center;padding:5px;width:68px}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:600;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-wp{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{padding:10px}.lobby-link-wp{gap:12px}.share-btn-wp .share-btn{border-radius:8px;height:52px;width:52px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-wp{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}";
1547
+ var css_248z = ".cb-lobby-modal-container{height:100%;left:0;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-indicator{backdrop-filter:blur(8px);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:24px;gap:8px;margin-bottom:16px;padding:8px 16px;width:fit-content}.lobby-indicator,.pulsing-dot{align-items:center;display:flex}.pulsing-dot{height:8px;justify-content:center;position:relative;width:8px}.pulsing-dot:after{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.pulsing-dot:after,.pulsing-dot:before{background:#10b981;border-radius:50%;content:\"\";height:100%;position:absolute;width:100%}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.indicator-text{color:#047857;font-size:11px;font-weight:800;letter-spacing:.5px;line-height:1;text-transform:uppercase}.lobby-status-section{display:flex;flex-direction:column;margin-bottom:24px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:#10b981;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{-webkit-text-fill-color:transparent;animation:gradient-flow 4s linear infinite;background-clip:text;-webkit-background-clip:text;background-image:linear-gradient(90deg,#1e293b,#2563eb 25%,#1e293b 50%);background-size:200% auto;color:#1e293b;font-size:16px;font-weight:700;line-height:1.5;margin-bottom:0}@keyframes gradient-flow{0%{background-position:200% 0}to{background-position:-200% 0}}.sub-title.completed{-webkit-text-fill-color:unset;background-clip:unset;-webkit-background-clip:unset;background-image:none;color:#1e293b}.connected-section{backdrop-filter:blur(4px);background:hsla(0,0%,100%,.05);border:1px solid hsla(0,0%,100%,.1);border-radius:24px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex-direction:column;gap:20px;margin-top:24px;padding:24px;transition:all .3s ease}.connected-section:hover{background:hsla(0,0%,100%,.1);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.link-share-container{display:flex;flex-direction:column}.link-share-wrapper{align-items:center;display:flex;gap:12px;width:100%}.lobby-link-box{align-items:center;backdrop-filter:blur(8px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.3);border-radius:14px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex:1;gap:16px;justify-content:space-between;min-height:50px;padding:16px 20px;transition:all .2s ease}.lobby-link-box:hover{background-color:hsla(0,0%,100%,.5);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-link-text{color:#71717a;font-size:10px;font-weight:700;letter-spacing:.6px;line-height:1.2;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:#64748b;cursor:pointer;display:flex;flex-shrink:0;font-size:13px;font-weight:600;gap:6px;justify-content:center;padding:0;transition:all .2s ease;white-space:nowrap}.copy-link-btn:hover{color:#1e293b}.copy-link-btn .copy-text{display:none}.copy-link-btn svg{flex-shrink:0;height:18px;width:18px}@media (min-width:640px){.copy-link-btn .copy-text{display:inline}}.lobby-link-url{color:#1e293b;font-size:15px;font-weight:700;line-height:1.4;word-break:break-all}.link-box-container{flex:1;min-width:0}.share-btn{align-items:center;background:#1e293b;border:none;border-radius:14px;box-shadow:0 2px 4px 0 rgba(0,0,0,.1);color:#fff;cursor:pointer;display:flex;flex-shrink:0;font-size:15px;font-weight:600;gap:8px;height:50px;justify-content:center;min-width:auto;padding:35px 24px;transition:all .2s ease}.share-btn .share-text{color:#fff;display:inline;font-size:15px}.share-btn svg{color:#fff}@media (max-width:640px){.share-btn{font-size:14px;height:50px;padding:0 18px}.share-btn .share-text{color:#fff;display:inline}}.share-btn:hover{background:#0f172a;box-shadow:0 4px 8px 0 rgba(0,0,0,.15);transform:translateY(-1px)}.share-btn:active{transform:scale(.95)}.share-btn svg{flex-shrink:0;height:18px;width:18px}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:600;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-section{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.share-btn{border-radius:8px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{min-height:48px;padding:12px 16px}.link-share-wrapper{gap:10px}.share-btn{border-radius:8px;font-size:13px;height:48px;min-width:auto;padding:0 14px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-section{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}@keyframes entrance-fade-in{0%{opacity:0}to{opacity:1}}@keyframes entrance-scale-in{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes entrance-text-fade-in{0%{opacity:0}to{opacity:1}}@keyframes exit-blur-scale{0%{filter:blur(0);opacity:1;transform:scale(1)}to{filter:blur(10px);opacity:0;transform:scale(1.1)}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.5}}.entrance-animation-overlay{align-items:center;animation:entrance-fade-in .8s ease-in-out forwards;background-color:#0f172a;color:#fff;display:flex;flex-direction:column;inset:0;justify-content:center;position:fixed;z-index:9999}.entrance-animation-overlay.exit{animation:exit-blur-scale .8s ease-in-out forwards}.entrance-content{align-items:center;animation:entrance-scale-in .5s ease-out .2s both;display:flex;flex-direction:column}.entrance-icon-box{align-items:center;background:#2563eb;border-radius:16px;box-shadow:0 10px 25px rgba(37,99,235,.2);display:flex;height:64px;justify-content:center;margin-bottom:24px;width:64px}.entrance-icon-box svg{color:#fff;height:32px;width:32px}.entrance-title{font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:12px}@media (min-width:768px){.entrance-title{font-size:48px}}.entrance-message{align-items:center;animation:entrance-text-fade-in .5s ease-out .5s both;color:#60a5fa;display:flex;font-size:18px;font-weight:500;gap:8px}.entrance-pulse-dot{animation:pulse-dot 1.5s ease-in-out infinite;background-color:#60a5fa;border-radius:50%;height:8px;width:8px}";
1394
1548
  styleInject(css_248z,{"insertAt":"bottom"});
1395
1549
 
1396
1550
  /**
@@ -1426,6 +1580,7 @@ var CoBuySDK = (function (exports) {
1426
1580
  this.handleSocketGroupUpdate = (event) => {
1427
1581
  var _a, _b, _c, _d, _e, _f, _g;
1428
1582
  const detail = event.detail || {};
1583
+ console.log("group completed", event, detail);
1429
1584
  const normalizeId = (id) => {
1430
1585
  if (id === undefined || id === null)
1431
1586
  return null;
@@ -1495,6 +1650,14 @@ var CoBuySDK = (function (exports) {
1495
1650
  status: isComplete ? "complete" : "active",
1496
1651
  isLocked: !isComplete,
1497
1652
  };
1653
+ // Add offline redemption data if available and group is complete
1654
+ if (isComplete) {
1655
+ const offlineRedemption = detail.offline_redemption;
1656
+ if (offlineRedemption && isValidOfflineRedemption(offlineRedemption)) {
1657
+ updatePayload.offlineRedemption = offlineRedemption;
1658
+ this.logger.info("Offline redemption data received in socket event");
1659
+ }
1660
+ }
1498
1661
  this.updateData(updatePayload);
1499
1662
  // Also update team card to reflect new member count
1500
1663
  this.updateTeamCard(participants, max);
@@ -1512,6 +1675,7 @@ var CoBuySDK = (function (exports) {
1512
1675
  currentMembers: data.currentMembers,
1513
1676
  totalMembers: data.totalMembers,
1514
1677
  timeLeft: data.timeLeft,
1678
+ offlineRedemption: data.offlineRedemption,
1515
1679
  });
1516
1680
  this.data = Object.assign({ groupNumber: "1000", status: "active", progress: 80, currentMembers: 4, totalMembers: 5, timeLeft: 1390, discount: "20% OFF", isLocked: true, activities: this.getDefaultActivities() }, data);
1517
1681
  // Normalize optional flags so undefined values don't override defaults
@@ -1542,7 +1706,7 @@ var CoBuySDK = (function (exports) {
1542
1706
  return true;
1543
1707
  }
1544
1708
  getRewardText(isLocked) {
1545
- return isLocked ? "🔓 Complete group to unlock reward." : "🎉 Reward unlocked!";
1709
+ return isLocked ? "🔓 Complete group to unlock discount." : "🎉 Discount unlocked!";
1546
1710
  }
1547
1711
  getTitleText(data) {
1548
1712
  var _a, _b;
@@ -1551,19 +1715,19 @@ var CoBuySDK = (function (exports) {
1551
1715
  const unlocked = !this.computeIsLocked(data);
1552
1716
  if (unlocked) {
1553
1717
  return {
1554
- title: "Reward unlocked!",
1555
- subtitle: "Group filled — enjoy your reward.",
1718
+ title: "Discount unlocked!",
1719
+ subtitle: "Group filled — enjoy your discount.",
1556
1720
  };
1557
1721
  }
1558
1722
  if (remaining <= 1) {
1559
1723
  return {
1560
1724
  title: "Just one more!",
1561
- subtitle: "Invite a friend to unlock your reward instantly.",
1725
+ subtitle: "Invite a friend to unlock your discount instantly.",
1562
1726
  };
1563
1727
  }
1564
1728
  return {
1565
1729
  title: "Almost there!",
1566
- subtitle: `Just ${remaining} more ${remaining === 1 ? "member" : "members"} — your reward unlocks instantly.`,
1730
+ subtitle: `Just ${remaining} more ${remaining === 1 ? "member" : "members"} — your discount unlocks instantly.`,
1567
1731
  };
1568
1732
  }
1569
1733
  /**
@@ -1613,6 +1777,62 @@ var CoBuySDK = (function (exports) {
1613
1777
  },
1614
1778
  ];
1615
1779
  }
1780
+ /**
1781
+ * Create entrance animation overlay
1782
+ */
1783
+ createEntranceAnimation() {
1784
+ const overlay = document.createElement("div");
1785
+ overlay.className = "entrance-animation-overlay";
1786
+ overlay.id = "entranceAnimationOverlay";
1787
+ const content = document.createElement("div");
1788
+ content.className = "entrance-content";
1789
+ // Icon box
1790
+ const iconBox = document.createElement("div");
1791
+ iconBox.className = "entrance-icon-box";
1792
+ iconBox.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>`;
1793
+ // Title
1794
+ const title = document.createElement("h1");
1795
+ title.className = "entrance-title";
1796
+ title.textContent = "Group Lobby";
1797
+ // Message with pulsing dot
1798
+ const message = document.createElement("p");
1799
+ message.className = "entrance-message";
1800
+ const pulseDot = document.createElement("span");
1801
+ pulseDot.className = "entrance-pulse-dot";
1802
+ const messageText = document.createElement("span");
1803
+ messageText.textContent = "Entering secure session...";
1804
+ message.appendChild(pulseDot);
1805
+ message.appendChild(messageText);
1806
+ content.appendChild(iconBox);
1807
+ content.appendChild(title);
1808
+ content.appendChild(message);
1809
+ overlay.appendChild(content);
1810
+ return overlay;
1811
+ }
1812
+ /**
1813
+ * Show entrance animation and auto-hide after duration
1814
+ */
1815
+ showEntranceAnimation(onComplete) {
1816
+ const overlay = this.createEntranceAnimation();
1817
+ document.body.appendChild(overlay);
1818
+ // After 2.5 seconds, trigger exit animation and remove
1819
+ setTimeout(() => {
1820
+ this.hideEntranceAnimation(overlay, onComplete);
1821
+ }, 2500);
1822
+ }
1823
+ /**
1824
+ * Hide entrance animation with exit effect
1825
+ */
1826
+ hideEntranceAnimation(overlay, onComplete) {
1827
+ overlay.classList.add("exit");
1828
+ // Remove after animation completes (800ms) and call callback
1829
+ setTimeout(() => {
1830
+ overlay.remove();
1831
+ if (onComplete) {
1832
+ onComplete();
1833
+ }
1834
+ }, 800);
1835
+ }
1616
1836
  /**
1617
1837
  * Create the modal DOM structure
1618
1838
  */
@@ -1635,9 +1855,12 @@ var CoBuySDK = (function (exports) {
1635
1855
  // Top section
1636
1856
  const topSection = this.createTopSection();
1637
1857
  mainContent.appendChild(topSection);
1638
- // Activity section
1639
- const activitySection = this.createActivitySection();
1640
- mainContent.appendChild(activitySection);
1858
+ // Activity section (hidden when offline redemption is present to keep layout compact)
1859
+ const hasOfflineRedemption = this.data.offlineRedemption && isValidOfflineRedemption(this.data.offlineRedemption);
1860
+ if (!hasOfflineRedemption) {
1861
+ const activitySection = this.createActivitySection();
1862
+ mainContent.appendChild(activitySection);
1863
+ }
1641
1864
  mainWrapper.appendChild(mainContent);
1642
1865
  modal.appendChild(background);
1643
1866
  modal.appendChild(mainWrapper);
@@ -1680,9 +1903,9 @@ var CoBuySDK = (function (exports) {
1680
1903
  // Title
1681
1904
  const titleWp = this.createTitleSection();
1682
1905
  leftWp.appendChild(titleWp);
1683
- // Link
1684
- const linkWp = this.createLinkSection();
1685
- leftWp.appendChild(linkWp);
1906
+ // Connected section (subtitle + link + share)
1907
+ const connectedSection = this.createConnectedSection();
1908
+ leftWp.appendChild(connectedSection);
1686
1909
  // Offer
1687
1910
  const offerBox = this.createOfferSection();
1688
1911
  leftWp.appendChild(offerBox);
@@ -1694,24 +1917,35 @@ var CoBuySDK = (function (exports) {
1694
1917
  createStatusSection() {
1695
1918
  var _a;
1696
1919
  const statusWp = document.createElement("div");
1697
- statusWp.className = "lobby-status-wp";
1920
+ statusWp.className = "lobby-status-section";
1698
1921
  // Use safe defaults so UI never shows undefined values
1699
1922
  const groupNum = (_a = this.data.groupNumber) !== null && _a !== void 0 ? _a : "1000";
1700
1923
  const status = this.data.status === "complete" ? "complete" : "active";
1701
- const statusLabel = document.createElement("div");
1702
- statusLabel.className = "lobby-status";
1703
- statusLabel.textContent = "Status";
1924
+ // New "You are in the lobby" indicator badge
1925
+ const lobbyIndicator = document.createElement("div");
1926
+ lobbyIndicator.className = "lobby-indicator";
1927
+ const pulsingDot = document.createElement("div");
1928
+ pulsingDot.className = "pulsing-dot";
1929
+ const indicatorText = document.createElement("span");
1930
+ indicatorText.className = "indicator-text";
1931
+ indicatorText.textContent = "YOU ARE IN THE LOBBY";
1932
+ lobbyIndicator.appendChild(pulsingDot);
1933
+ lobbyIndicator.appendChild(indicatorText);
1934
+ // Status row with badge and group number
1935
+ const statusRow = document.createElement("div");
1936
+ statusRow.className = "lobby-status-wp";
1704
1937
  // Render only the current status chip
1705
1938
  const statusChip = document.createElement("div");
1706
1939
  statusChip.className = `lobby-status ${status}`;
1707
1940
  statusChip.id = "lobbyStatusChip";
1708
- statusChip.textContent = status === "complete" ? "Complete" : "Active";
1941
+ statusChip.textContent = status === "complete" ? "COMPLETED" : "ACTIVE";
1709
1942
  const groupNumber = document.createElement("div");
1710
1943
  groupNumber.className = "lobby-number";
1711
1944
  groupNumber.textContent = `Group #${groupNum}`;
1712
- statusWp.appendChild(statusLabel);
1713
- statusWp.appendChild(statusChip);
1714
- statusWp.appendChild(groupNumber);
1945
+ statusRow.appendChild(statusChip);
1946
+ statusRow.appendChild(groupNumber);
1947
+ statusWp.appendChild(lobbyIndicator);
1948
+ statusWp.appendChild(statusRow);
1715
1949
  return statusWp;
1716
1950
  }
1717
1951
  /**
@@ -1724,24 +1958,49 @@ var CoBuySDK = (function (exports) {
1724
1958
  const title = document.createElement("h2");
1725
1959
  title.id = "lobbyTitleText";
1726
1960
  title.textContent = titleContent.title;
1961
+ titleWp.appendChild(title);
1962
+ return titleWp;
1963
+ }
1964
+ /**
1965
+ * Create connected section (subtitle + link + share)
1966
+ */
1967
+ createConnectedSection() {
1968
+ const connectedSection = document.createElement("div");
1969
+ connectedSection.className = "connected-section";
1970
+ connectedSection.id = "lobbyConnectedSection";
1971
+ const titleContent = this.getTitleText(this.data);
1972
+ // Subtitle
1727
1973
  const subtitle = document.createElement("p");
1728
1974
  subtitle.className = "sub-title";
1729
1975
  subtitle.id = "lobbySubtitleText";
1730
1976
  subtitle.textContent = titleContent.subtitle;
1731
- titleWp.appendChild(title);
1732
- titleWp.appendChild(subtitle);
1733
- return titleWp;
1977
+ connectedSection.appendChild(subtitle);
1978
+ // Check if group is fulfilled and has offline redemption
1979
+ const isComplete = !this.computeIsLocked(this.data);
1980
+ const hasOfflineRedemption = isComplete &&
1981
+ this.data.offlineRedemption &&
1982
+ isValidOfflineRedemption(this.data.offlineRedemption);
1983
+ if (hasOfflineRedemption) {
1984
+ // Show offline redemption view with integrated actions
1985
+ const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
1986
+ connectedSection.appendChild(offlineSection);
1987
+ }
1988
+ else {
1989
+ // Show link and share container
1990
+ const linkShareContainer = document.createElement("div");
1991
+ linkShareContainer.className = "link-share-container";
1992
+ const linkWp = this.createLinkSection();
1993
+ linkShareContainer.appendChild(linkWp);
1994
+ connectedSection.appendChild(linkShareContainer);
1995
+ }
1996
+ return connectedSection;
1734
1997
  }
1735
1998
  /**
1736
- * Create link section
1999
+ * Create link section (just the link box and share button, no wrapper)
1737
2000
  */
1738
2001
  createLinkSection() {
1739
- const linkWp = document.createElement("div");
1740
- linkWp.className = "lobby-link-wp";
1741
- linkWp.id = "lobbyLinkSection";
1742
- if (!this.data.isLocked) {
1743
- linkWp.style.display = "none";
1744
- }
2002
+ const linkShareWrapper = document.createElement("div");
2003
+ linkShareWrapper.className = "link-share-wrapper";
1745
2004
  // Link box
1746
2005
  const linkBox = document.createElement("div");
1747
2006
  linkBox.className = "lobby-link-box";
@@ -1756,23 +2015,324 @@ var CoBuySDK = (function (exports) {
1756
2015
  linkUrl.textContent = this.data.groupLink || `cobuy.group/lobby/${this.data.productId}`;
1757
2016
  linkBoxInr.appendChild(linkText);
1758
2017
  linkBoxInr.appendChild(linkUrl);
1759
- const copyBtn = document.createElement("div");
2018
+ const copyBtn = document.createElement("button");
1760
2019
  copyBtn.className = "copy-link-btn";
1761
- copyBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy w-4 h-4 sm:w-5 sm:h-5"><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>`;
2020
+ copyBtn.innerHTML = `<span class="copy-text">Copy</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><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>`;
1762
2021
  copyBtn.addEventListener("click", () => this.copyLink());
1763
2022
  linkBox.appendChild(linkBoxInr);
1764
2023
  linkBox.appendChild(copyBtn);
1765
2024
  // Share button
1766
- const shareBtn = document.createElement("div");
1767
- shareBtn.className = "share-btn-wp";
1768
- const shareBtnInner = document.createElement("div");
2025
+ const shareBtnInner = document.createElement("button");
1769
2026
  shareBtnInner.className = "share-btn";
1770
- shareBtnInner.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-share w-6 h-6 sm:w-7 sm:h-7"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path><polyline points="16 6 12 2 8 6"></polyline><line x1="12" x2="12" y1="2" y2="15"></line></svg>`;
2027
+ shareBtnInner.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-share"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path><polyline points="16 6 12 2 8 6"></polyline><line x1="12" x2="12" y1="2" y2="15"></line></svg><span class="share-text">Share</span>`;
1771
2028
  shareBtnInner.addEventListener("click", () => this.share());
1772
- shareBtn.appendChild(shareBtnInner);
1773
- linkWp.appendChild(linkBox);
1774
- linkWp.appendChild(shareBtn);
1775
- return linkWp;
2029
+ linkShareWrapper.appendChild(linkBox);
2030
+ linkShareWrapper.appendChild(shareBtnInner);
2031
+ return linkShareWrapper;
2032
+ }
2033
+ /**
2034
+ * Create offline redemption section
2035
+ */
2036
+ createOfflineRedemptionSection(offlineRedemption) {
2037
+ const section = document.createElement("div");
2038
+ section.className = "offline-redemption-section";
2039
+ section.id = "lobbyOfflineRedemptionSection";
2040
+ // Top row: QR + Code side by side
2041
+ const topRow = document.createElement("div");
2042
+ topRow.className = "offline-top-row";
2043
+ // QR Code container
2044
+ const qrContainer = document.createElement("div");
2045
+ qrContainer.className = "offline-qr-container";
2046
+ const qrImage = document.createElement("img");
2047
+ qrImage.className = "offline-qr-image";
2048
+ qrImage.alt = "Redemption QR Code";
2049
+ qrImage.src = buildQRUrl(offlineRedemption.qr_code_data);
2050
+ // Handle QR image load errors
2051
+ qrImage.addEventListener("error", () => {
2052
+ qrImage.style.display = "none";
2053
+ const fallbackText = document.createElement("div");
2054
+ fallbackText.className = "offline-qr-fallback";
2055
+ fallbackText.textContent = "QR Code Unavailable";
2056
+ qrContainer.appendChild(fallbackText);
2057
+ });
2058
+ qrContainer.appendChild(qrImage);
2059
+ // Redemption code box
2060
+ const codeBox = document.createElement("div");
2061
+ codeBox.className = "offline-code-box";
2062
+ const codeLabel = document.createElement("p");
2063
+ codeLabel.className = "offline-code-label";
2064
+ codeLabel.textContent = "Redemption Code";
2065
+ const codeValue = document.createElement("div");
2066
+ codeValue.className = "offline-code-value";
2067
+ codeValue.textContent = offlineRedemption.redemption_code;
2068
+ const copyCodeBtn = document.createElement("button");
2069
+ copyCodeBtn.className = "offline-copy-code-btn";
2070
+ 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>`;
2071
+ copyCodeBtn.addEventListener("click", () => this.copyOfflineRedemptionCode(offlineRedemption.redemption_code, copyCodeBtn));
2072
+ const codeRow = document.createElement("div");
2073
+ codeRow.className = "offline-code-row";
2074
+ codeRow.appendChild(codeValue);
2075
+ codeRow.appendChild(copyCodeBtn);
2076
+ codeBox.appendChild(codeLabel);
2077
+ codeBox.appendChild(codeRow);
2078
+ topRow.appendChild(qrContainer);
2079
+ topRow.appendChild(codeBox);
2080
+ // Expiry info - compact single line
2081
+ const expiryInfo = document.createElement("div");
2082
+ expiryInfo.className = "offline-expiry-info";
2083
+ expiryInfo.innerHTML = `<span class="offline-expiry-label">Valid Until:</span> <span class="offline-expiry-value">${formatExpiryDate(offlineRedemption.offline_expires_at)}</span>`;
2084
+ // Action buttons row
2085
+ const actionsRow = document.createElement("div");
2086
+ actionsRow.className = "offline-actions-row";
2087
+ // Download QR button
2088
+ const downloadQRBtn = document.createElement("button");
2089
+ downloadQRBtn.className = "offline-download-qr-btn";
2090
+ downloadQRBtn.textContent = "Download QR";
2091
+ downloadQRBtn.addEventListener("click", () => this.downloadOfflineQR(offlineRedemption, downloadQRBtn));
2092
+ // Online checkout button
2093
+ const onlineCheckoutBtn = document.createElement("button");
2094
+ onlineCheckoutBtn.className = "offline-online-checkout-btn";
2095
+ onlineCheckoutBtn.textContent = "Checkout Online";
2096
+ onlineCheckoutBtn.addEventListener("click", () => {
2097
+ if (this.data.groupLink) {
2098
+ window.open(this.data.groupLink, "_blank");
2099
+ }
2100
+ });
2101
+ actionsRow.appendChild(downloadQRBtn);
2102
+ actionsRow.appendChild(onlineCheckoutBtn);
2103
+ section.appendChild(topRow);
2104
+ section.appendChild(expiryInfo);
2105
+ section.appendChild(actionsRow);
2106
+ // Inject styles for offline redemption section
2107
+ this.injectOfflineRedemptionStyles();
2108
+ return section;
2109
+ }
2110
+ /**
2111
+ * Handle copying offline redemption code
2112
+ */
2113
+ async copyOfflineRedemptionCode(code, button) {
2114
+ const originalContent = button.innerHTML;
2115
+ const success = await copyRedemptionCode(code);
2116
+ if (success) {
2117
+ button.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"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
2118
+ setTimeout(() => {
2119
+ button.innerHTML = originalContent;
2120
+ }, 2000);
2121
+ }
2122
+ }
2123
+ /**
2124
+ * Handle downloading offline QR code
2125
+ */
2126
+ async downloadOfflineQR(offlineRedemption, button) {
2127
+ const originalText = button.textContent;
2128
+ button.disabled = true;
2129
+ button.textContent = "Downloading...";
2130
+ const success = await downloadQRCode(buildQRUrl(offlineRedemption.qr_code_data), "redemption-qr.png");
2131
+ button.textContent = success ? "Downloaded!" : "Failed";
2132
+ setTimeout(() => {
2133
+ button.textContent = originalText;
2134
+ button.disabled = false;
2135
+ }, 2000);
2136
+ }
2137
+ /**
2138
+ * Inject styles for offline redemption section
2139
+ */
2140
+ injectOfflineRedemptionStyles() {
2141
+ if (document.getElementById("lobby-offline-redemption-styles")) {
2142
+ return;
2143
+ }
2144
+ const style = document.createElement("style");
2145
+ style.id = "lobby-offline-redemption-styles";
2146
+ style.textContent = `
2147
+ .offline-redemption-section {
2148
+ display: flex;
2149
+ flex-direction: column;
2150
+ gap: 14px;
2151
+ padding: 16px;
2152
+ background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
2153
+ border: 1.5px solid #e5e7eb;
2154
+ border-radius: 12px;
2155
+ margin-top: 12px;
2156
+ }
2157
+
2158
+ .offline-top-row {
2159
+ display: flex;
2160
+ gap: 14px;
2161
+ align-items: stretch;
2162
+ }
2163
+
2164
+ .offline-qr-container {
2165
+ flex: 0 0 120px;
2166
+ display: flex;
2167
+ justify-content: center;
2168
+ align-items: center;
2169
+ background: white;
2170
+ padding: 10px;
2171
+ border-radius: 8px;
2172
+ border: 1.5px solid #e5e7eb;
2173
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
2174
+ }
2175
+
2176
+ .offline-qr-image {
2177
+ max-width: 100px;
2178
+ max-height: 100px;
2179
+ width: auto;
2180
+ height: auto;
2181
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.08));
2182
+ }
2183
+
2184
+ .offline-qr-fallback {
2185
+ color: #9ca3af;
2186
+ font-size: 11px;
2187
+ text-align: center;
2188
+ }
2189
+
2190
+ .offline-code-box {
2191
+ flex: 1;
2192
+ display: flex;
2193
+ flex-direction: column;
2194
+ gap: 8px;
2195
+ justify-content: center;
2196
+ }
2197
+
2198
+ .offline-code-row {
2199
+ position: relative;
2200
+ display: flex;
2201
+ align-items: center;
2202
+ }
2203
+
2204
+ .offline-code-label {
2205
+ margin: 0;
2206
+ font-size: 11px;
2207
+ font-weight: 700;
2208
+ color: #1f2937;
2209
+ text-transform: uppercase;
2210
+ letter-spacing: 0.5px;
2211
+ }
2212
+
2213
+ .offline-code-value {
2214
+ font-family: 'Courier New', monospace;
2215
+ font-size: 14px;
2216
+ font-weight: 700;
2217
+ color: #111827;
2218
+ padding: 10px 40px 10px 12px;
2219
+ background: white;
2220
+ border: 1.5px solid #d1d5db;
2221
+ border-radius: 8px;
2222
+ text-align: center;
2223
+ letter-spacing: 1.5px;
2224
+ position: relative;
2225
+ display: flex;
2226
+ align-items: center;
2227
+ justify-content: center;
2228
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
2229
+ transition: all 0.2s;
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);
2239
+ }
2240
+
2241
+ .offline-copy-code-btn {
2242
+ position: absolute;
2243
+ right: 6px;
2244
+ top: 50%;
2245
+ transform: translateY(-50%);
2246
+ background: none;
2247
+ border: none;
2248
+ padding: 5px 8px;
2249
+ cursor: pointer;
2250
+ color: #6b7280;
2251
+ display: flex;
2252
+ align-items: center;
2253
+ justify-content: center;
2254
+ transition: all 0.2s;
2255
+ border-radius: 4px;
2256
+ }
2257
+
2258
+ .offline-copy-code-btn:hover {
2259
+ color: #3b82f6;
2260
+ background: #eff6ff;
2261
+ }
2262
+
2263
+ .offline-expiry-info {
2264
+ font-size: 11px;
2265
+ color: #6b7280;
2266
+ text-align: center;
2267
+ padding: 6px 0;
2268
+ }
2269
+
2270
+ .offline-expiry-label {
2271
+ font-weight: 700;
2272
+ text-transform: uppercase;
2273
+ letter-spacing: 0.5px;
2274
+ color: #1f2937;
2275
+ }
2276
+
2277
+ .offline-expiry-value {
2278
+ font-weight: 600;
2279
+ color: #374151;
2280
+ }
2281
+
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;
2291
+ border: none;
2292
+ border-radius: 8px;
2293
+ font-size: 13px;
2294
+ font-weight: 700;
2295
+ cursor: pointer;
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);
2305
+ }
2306
+
2307
+ .offline-download-qr-btn:hover:not(:disabled) {
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);
2328
+ }
2329
+
2330
+ .offline-download-qr-btn:disabled {
2331
+ opacity: 0.7;
2332
+ cursor: not-allowed;
2333
+ }
2334
+ `;
2335
+ document.head.appendChild(style);
1776
2336
  }
1777
2337
  /**
1778
2338
  * Create offer section
@@ -2411,24 +2971,27 @@ var CoBuySDK = (function (exports) {
2411
2971
  groupNumber: this.data.groupNumber,
2412
2972
  status: this.data.status,
2413
2973
  });
2414
- this.modalElement = this.createModalStructure();
2415
- document.body.appendChild(this.modalElement);
2416
- // Subscribe to realtime socket events
2417
- this.subscribeToSocketEvents();
2418
- // If socket is available and connected, subscribe to this group
2419
- if (this.socketManager && this.socketManager.isConnected() && this.currentGroupId) {
2420
- this.logger.info(`[LobbyModal] Socket connected, subscribing to group ${this.currentGroupId}`);
2421
- this.socketManager.subscribeToGroup(this.currentGroupId);
2422
- }
2423
- else if (this.socketManager && !this.socketManager.isConnected()) {
2424
- this.logger.warn("[LobbyModal] Socket manager not connected yet");
2425
- }
2426
- // Start timers and animations
2427
- this.startTimer();
2428
- this.startActivityAnimation();
2429
- // Track analytics
2430
- if (this.analyticsClient) ;
2431
- this.logger.info(`Lobby modal opened for product: ${this.data.productId}`);
2974
+ // Show entrance animation, then open modal after animation completes
2975
+ this.showEntranceAnimation(() => {
2976
+ this.modalElement = this.createModalStructure();
2977
+ document.body.appendChild(this.modalElement);
2978
+ // Subscribe to realtime socket events
2979
+ this.subscribeToSocketEvents();
2980
+ // If socket is available and connected, subscribe to this group
2981
+ if (this.socketManager && this.socketManager.isConnected() && this.currentGroupId) {
2982
+ this.logger.info(`[LobbyModal] Socket connected, subscribing to group ${this.currentGroupId}`);
2983
+ this.socketManager.subscribeToGroup(this.currentGroupId);
2984
+ }
2985
+ else if (this.socketManager && !this.socketManager.isConnected()) {
2986
+ this.logger.warn("[LobbyModal] Socket manager not connected yet");
2987
+ }
2988
+ // Start timers and animations
2989
+ this.startTimer();
2990
+ this.startActivityAnimation();
2991
+ // Track analytics
2992
+ if (this.analyticsClient) ;
2993
+ this.logger.info(`Lobby modal opened for product: ${this.data.productId}`);
2994
+ });
2432
2995
  }
2433
2996
  /**
2434
2997
  * Close the modal
@@ -2474,6 +3037,10 @@ var CoBuySDK = (function (exports) {
2474
3037
  this.updateLockUI(!!this.data.isLocked);
2475
3038
  this.updateTitleUI(this.data);
2476
3039
  this.updateLinkVisibility(!!this.data.isLocked);
3040
+ // Update offline redemption visibility if data changed
3041
+ if (data.offlineRedemption !== undefined || data.status !== undefined) {
3042
+ this.updateOfflineRedemptionVisibility();
3043
+ }
2477
3044
  // Update progress if changed
2478
3045
  if (data.progress !== undefined) {
2479
3046
  const progressFill = document.getElementById("progressFill");
@@ -2548,6 +3115,42 @@ var CoBuySDK = (function (exports) {
2548
3115
  return;
2549
3116
  linkSection.style.display = isLocked ? "" : "none";
2550
3117
  }
3118
+ /**
3119
+ * Update offline redemption visibility when group is fulfilled
3120
+ */
3121
+ updateOfflineRedemptionVisibility() {
3122
+ const connectedSection = document.getElementById("lobbyConnectedSection");
3123
+ if (!connectedSection)
3124
+ return;
3125
+ const isComplete = !this.computeIsLocked(this.data);
3126
+ const hasOfflineRedemption = this.data.offlineRedemption && isValidOfflineRedemption(this.data.offlineRedemption);
3127
+ // Get existing elements
3128
+ const existingOffline = connectedSection.querySelector(".offline-redemption-section");
3129
+ const existingLink = connectedSection.querySelector(".link-share-container");
3130
+ if (isComplete && hasOfflineRedemption) {
3131
+ // Show offline redemption, hide link/share
3132
+ if (existingLink) {
3133
+ existingLink.remove();
3134
+ }
3135
+ if (!existingOffline) {
3136
+ const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
3137
+ connectedSection.appendChild(offlineSection);
3138
+ }
3139
+ }
3140
+ else {
3141
+ // Show link/share, hide offline redemption
3142
+ if (existingOffline) {
3143
+ existingOffline.remove();
3144
+ }
3145
+ if (!existingLink) {
3146
+ const linkShareContainer = document.createElement("div");
3147
+ linkShareContainer.className = "link-share-container";
3148
+ const linkWp = this.createLinkSection();
3149
+ linkShareContainer.appendChild(linkWp);
3150
+ connectedSection.appendChild(linkShareContainer);
3151
+ }
3152
+ }
3153
+ }
2551
3154
  /**
2552
3155
  * Subscribe to socket events
2553
3156
  */
@@ -2584,7 +3187,7 @@ var CoBuySDK = (function (exports) {
2584
3187
  break;
2585
3188
  case "group:fulfilled":
2586
3189
  emoji = "🎁";
2587
- action = "unlocked the reward – group filled!";
3190
+ action = "unlocked the discount – group filled!";
2588
3191
  break;
2589
3192
  case "group:created":
2590
3193
  emoji = "🛒";
@@ -2652,6 +3255,405 @@ var CoBuySDK = (function (exports) {
2652
3255
  }
2653
3256
  }
2654
3257
 
3258
+ /**
3259
+ * Offline Redemption Modal
3260
+ * Displays QR code and redemption code for fulfilled groups
3261
+ */
3262
+ class OfflineRedemptionModal {
3263
+ constructor(offlineRedemption, onClose) {
3264
+ this.modalElement = null;
3265
+ this.offlineRedemption = offlineRedemption;
3266
+ this.onClose = onClose;
3267
+ }
3268
+ /**
3269
+ * Open the modal
3270
+ */
3271
+ open() {
3272
+ if (this.modalElement && document.body.contains(this.modalElement)) {
3273
+ return; // Already open
3274
+ }
3275
+ this.modalElement = this.createModalStructure();
3276
+ document.body.appendChild(this.modalElement);
3277
+ // Trigger entrance animation
3278
+ requestAnimationFrame(() => {
3279
+ var _a;
3280
+ (_a = this.modalElement) === null || _a === void 0 ? void 0 : _a.classList.add("offline-redemption-modal-open");
3281
+ });
3282
+ }
3283
+ /**
3284
+ * Close the modal
3285
+ */
3286
+ close() {
3287
+ if (!this.modalElement)
3288
+ return;
3289
+ this.modalElement.classList.remove("offline-redemption-modal-open");
3290
+ // Remove after animation completes
3291
+ setTimeout(() => {
3292
+ var _a;
3293
+ if (this.modalElement && document.body.contains(this.modalElement)) {
3294
+ document.body.removeChild(this.modalElement);
3295
+ this.modalElement = null;
3296
+ }
3297
+ (_a = this.onClose) === null || _a === void 0 ? void 0 : _a.call(this);
3298
+ }, 300);
3299
+ }
3300
+ /**
3301
+ * Create the modal DOM structure
3302
+ */
3303
+ createModalStructure() {
3304
+ const container = document.createElement("div");
3305
+ container.className = "offline-redemption-modal-overlay";
3306
+ // Backdrop
3307
+ const backdrop = document.createElement("div");
3308
+ backdrop.className = "offline-redemption-backdrop";
3309
+ backdrop.addEventListener("click", () => this.close());
3310
+ // Modal content
3311
+ const modal = document.createElement("div");
3312
+ modal.className = "offline-redemption-modal";
3313
+ // Close button
3314
+ const closeBtn = document.createElement("button");
3315
+ closeBtn.className = "offline-redemption-close-btn";
3316
+ closeBtn.setAttribute("aria-label", "Close modal");
3317
+ closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>`;
3318
+ closeBtn.addEventListener("click", () => this.close());
3319
+ // Header
3320
+ const header = document.createElement("div");
3321
+ header.className = "offline-redemption-header";
3322
+ const title = document.createElement("h2");
3323
+ title.className = "offline-redemption-title";
3324
+ title.textContent = "Redeem In-Store";
3325
+ header.appendChild(title);
3326
+ header.appendChild(closeBtn);
3327
+ // Body
3328
+ const body = document.createElement("div");
3329
+ body.className = "offline-redemption-body";
3330
+ // QR Code section
3331
+ const qrSection = document.createElement("div");
3332
+ qrSection.className = "offline-redemption-qr-section";
3333
+ const qrImage = document.createElement("img");
3334
+ qrImage.className = "offline-redemption-qr-image";
3335
+ qrImage.alt = "Redemption QR Code";
3336
+ qrImage.src = buildQRUrl(this.offlineRedemption.qr_code_data);
3337
+ // QR image fallback handler
3338
+ qrImage.addEventListener("error", () => {
3339
+ qrImage.style.display = "none";
3340
+ const errorMsg = document.createElement("div");
3341
+ errorMsg.className = "offline-redemption-qr-error";
3342
+ errorMsg.textContent = "QR code unavailable";
3343
+ qrSection.appendChild(errorMsg);
3344
+ });
3345
+ qrSection.appendChild(qrImage);
3346
+ // Redemption code section
3347
+ const codeSection = document.createElement("div");
3348
+ codeSection.className = "offline-redemption-code-section";
3349
+ const codeLabel = document.createElement("p");
3350
+ codeLabel.className = "offline-redemption-code-label";
3351
+ codeLabel.textContent = "Redemption Code:";
3352
+ const codeValue = document.createElement("div");
3353
+ codeValue.className = "offline-redemption-code-value";
3354
+ codeValue.textContent = this.offlineRedemption.redemption_code;
3355
+ codeSection.appendChild(codeLabel);
3356
+ codeSection.appendChild(codeValue);
3357
+ // Expiry section
3358
+ const expirySection = document.createElement("div");
3359
+ expirySection.className = "offline-redemption-expiry-section";
3360
+ const expiryLabel = document.createElement("p");
3361
+ expiryLabel.className = "offline-redemption-expiry-label";
3362
+ expiryLabel.textContent = "Valid Until:";
3363
+ const expiryValue = document.createElement("p");
3364
+ expiryValue.className = "offline-redemption-expiry-value";
3365
+ const isValid = isRedemptionValid(this.offlineRedemption.offline_expires_at);
3366
+ expiryValue.textContent = formatExpiryDate(this.offlineRedemption.offline_expires_at);
3367
+ if (!isValid) {
3368
+ expiryValue.classList.add("expired");
3369
+ }
3370
+ expirySection.appendChild(expiryLabel);
3371
+ expirySection.appendChild(expiryValue);
3372
+ body.appendChild(qrSection);
3373
+ body.appendChild(codeSection);
3374
+ body.appendChild(expirySection);
3375
+ // Actions
3376
+ const actions = document.createElement("div");
3377
+ actions.className = "offline-redemption-actions";
3378
+ const downloadBtn = document.createElement("button");
3379
+ downloadBtn.className = "offline-redemption-action-btn download-btn";
3380
+ downloadBtn.textContent = "Download QR";
3381
+ downloadBtn.addEventListener("click", () => this.handleDownloadQR(downloadBtn));
3382
+ const copyBtn = document.createElement("button");
3383
+ copyBtn.className = "offline-redemption-action-btn copy-btn";
3384
+ copyBtn.textContent = "Copy Code";
3385
+ copyBtn.addEventListener("click", () => this.handleCopyCode(copyBtn));
3386
+ actions.appendChild(downloadBtn);
3387
+ actions.appendChild(copyBtn);
3388
+ // Assemble modal
3389
+ modal.appendChild(header);
3390
+ modal.appendChild(body);
3391
+ modal.appendChild(actions);
3392
+ container.appendChild(backdrop);
3393
+ container.appendChild(modal);
3394
+ // Inject styles
3395
+ this.injectStyles();
3396
+ return container;
3397
+ }
3398
+ /**
3399
+ * Handle download QR button click
3400
+ */
3401
+ async handleDownloadQR(button) {
3402
+ const originalText = button.textContent;
3403
+ button.disabled = true;
3404
+ button.textContent = "Downloading...";
3405
+ const success = await downloadQRCode(buildQRUrl(this.offlineRedemption.qr_code_data), "redemption-qr.png");
3406
+ button.textContent = success ? "Downloaded!" : "Download Failed";
3407
+ setTimeout(() => {
3408
+ button.textContent = originalText;
3409
+ button.disabled = false;
3410
+ }, 2000);
3411
+ }
3412
+ /**
3413
+ * Handle copy code button click
3414
+ */
3415
+ async handleCopyCode(button) {
3416
+ const originalText = button.textContent;
3417
+ button.disabled = true;
3418
+ const success = await copyRedemptionCode(this.offlineRedemption.redemption_code);
3419
+ button.textContent = success ? "Copied!" : "Copy Failed";
3420
+ setTimeout(() => {
3421
+ button.textContent = originalText;
3422
+ button.disabled = false;
3423
+ }, 2000);
3424
+ }
3425
+ /**
3426
+ * Inject modal styles
3427
+ */
3428
+ injectStyles() {
3429
+ // Check if styles already injected
3430
+ if (document.getElementById("offline-redemption-modal-styles")) {
3431
+ return;
3432
+ }
3433
+ const style = document.createElement("style");
3434
+ style.id = "offline-redemption-modal-styles";
3435
+ style.textContent = `
3436
+ .offline-redemption-modal-overlay {
3437
+ position: fixed;
3438
+ top: 0;
3439
+ left: 0;
3440
+ right: 0;
3441
+ bottom: 0;
3442
+ display: flex;
3443
+ align-items: center;
3444
+ justify-content: center;
3445
+ z-index: 10000;
3446
+ opacity: 0;
3447
+ transition: opacity 0.3s ease-out;
3448
+ }
3449
+
3450
+ .offline-redemption-modal-overlay.offline-redemption-modal-open {
3451
+ opacity: 1;
3452
+ }
3453
+
3454
+ .offline-redemption-backdrop {
3455
+ position: absolute;
3456
+ top: 0;
3457
+ left: 0;
3458
+ right: 0;
3459
+ bottom: 0;
3460
+ background: rgba(0, 0, 0, 0.5);
3461
+ cursor: pointer;
3462
+ }
3463
+
3464
+ .offline-redemption-modal {
3465
+ position: relative;
3466
+ z-index: 10001;
3467
+ background: white;
3468
+ border-radius: 12px;
3469
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
3470
+ max-width: 400px;
3471
+ width: 90%;
3472
+ max-height: 90vh;
3473
+ overflow-y: auto;
3474
+ animation: slideUp 0.3s ease-out;
3475
+ }
3476
+
3477
+ @keyframes slideUp {
3478
+ from {
3479
+ opacity: 0;
3480
+ transform: translateY(40px);
3481
+ }
3482
+ to {
3483
+ opacity: 1;
3484
+ transform: translateY(0);
3485
+ }
3486
+ }
3487
+
3488
+ .offline-redemption-header {
3489
+ display: flex;
3490
+ justify-content: space-between;
3491
+ align-items: center;
3492
+ padding: 20px;
3493
+ border-bottom: 1px solid #f0f0f0;
3494
+ }
3495
+
3496
+ .offline-redemption-title {
3497
+ margin: 0;
3498
+ font-size: 20px;
3499
+ font-weight: 600;
3500
+ color: #111827;
3501
+ }
3502
+
3503
+ .offline-redemption-close-btn {
3504
+ background: none;
3505
+ border: none;
3506
+ padding: 0;
3507
+ cursor: pointer;
3508
+ display: flex;
3509
+ align-items: center;
3510
+ justify-content: center;
3511
+ color: #6b7280;
3512
+ transition: color 0.2s;
3513
+ }
3514
+
3515
+ .offline-redemption-close-btn:hover {
3516
+ color: #111827;
3517
+ }
3518
+
3519
+ .offline-redemption-close-btn svg {
3520
+ width: 24px;
3521
+ height: 24px;
3522
+ }
3523
+
3524
+ .offline-redemption-body {
3525
+ padding: 24px 20px;
3526
+ display: flex;
3527
+ flex-direction: column;
3528
+ gap: 20px;
3529
+ }
3530
+
3531
+ .offline-redemption-qr-section {
3532
+ display: flex;
3533
+ justify-content: center;
3534
+ background: #f9fafb;
3535
+ padding: 16px;
3536
+ border-radius: 8px;
3537
+ min-height: 200px;
3538
+ align-items: center;
3539
+ }
3540
+
3541
+ .offline-redemption-qr-image {
3542
+ max-width: 100%;
3543
+ max-height: 200px;
3544
+ width: auto;
3545
+ height: auto;
3546
+ }
3547
+
3548
+ .offline-redemption-qr-error {
3549
+ text-align: center;
3550
+ color: #6b7280;
3551
+ font-size: 14px;
3552
+ }
3553
+
3554
+ .offline-redemption-code-section,
3555
+ .offline-redemption-expiry-section {
3556
+ display: flex;
3557
+ flex-direction: column;
3558
+ gap: 8px;
3559
+ }
3560
+
3561
+ .offline-redemption-code-label,
3562
+ .offline-redemption-expiry-label {
3563
+ margin: 0;
3564
+ font-size: 12px;
3565
+ font-weight: 600;
3566
+ color: #6b7280;
3567
+ text-transform: uppercase;
3568
+ letter-spacing: 0.5px;
3569
+ }
3570
+
3571
+ .offline-redemption-code-value {
3572
+ font-family: monospace;
3573
+ font-size: 16px;
3574
+ font-weight: 600;
3575
+ color: #111827;
3576
+ padding: 12px;
3577
+ background: #f9fafb;
3578
+ border: 1px solid #e5e7eb;
3579
+ border-radius: 6px;
3580
+ text-align: center;
3581
+ letter-spacing: 2px;
3582
+ }
3583
+
3584
+ .offline-redemption-expiry-value {
3585
+ margin: 0;
3586
+ font-size: 14px;
3587
+ color: #374151;
3588
+ }
3589
+
3590
+ .offline-redemption-expiry-value.expired {
3591
+ color: #dc2626;
3592
+ font-weight: 600;
3593
+ }
3594
+
3595
+ .offline-redemption-actions {
3596
+ display: flex;
3597
+ gap: 12px;
3598
+ padding: 20px;
3599
+ border-top: 1px solid #f0f0f0;
3600
+ }
3601
+
3602
+ .offline-redemption-action-btn {
3603
+ flex: 1;
3604
+ padding: 12px 16px;
3605
+ border: none;
3606
+ border-radius: 6px;
3607
+ font-size: 14px;
3608
+ font-weight: 600;
3609
+ cursor: pointer;
3610
+ transition: all 0.2s;
3611
+ }
3612
+
3613
+ .download-btn {
3614
+ background: #3b82f6;
3615
+ color: white;
3616
+ }
3617
+
3618
+ .download-btn:hover:not(:disabled) {
3619
+ background: #2563eb;
3620
+ }
3621
+
3622
+ .copy-btn {
3623
+ background: #f3f4f6;
3624
+ color: #111827;
3625
+ border: 1px solid #e5e7eb;
3626
+ }
3627
+
3628
+ .copy-btn:hover:not(:disabled) {
3629
+ background: #e5e7eb;
3630
+ }
3631
+
3632
+ .offline-redemption-action-btn:disabled {
3633
+ opacity: 0.7;
3634
+ cursor: not-allowed;
3635
+ }
3636
+
3637
+ /* Mobile responsiveness */
3638
+ @media (max-width: 480px) {
3639
+ .offline-redemption-modal {
3640
+ width: 95%;
3641
+ max-height: 95vh;
3642
+ }
3643
+
3644
+ .offline-redemption-body {
3645
+ padding: 16px;
3646
+ }
3647
+
3648
+ .offline-redemption-actions {
3649
+ padding: 16px;
3650
+ }
3651
+ }
3652
+ `;
3653
+ document.head.appendChild(style);
3654
+ }
3655
+ }
3656
+
2655
3657
  /// <reference lib="dom" />
2656
3658
  /**
2657
3659
  * Widget state enumeration for tracking render lifecycle
@@ -2686,6 +3688,8 @@ var CoBuySDK = (function (exports) {
2686
3688
  this.lastGroupDataRefreshTime = 0;
2687
3689
  this.GROUP_REFRESH_DEBOUNCE = 1000; // 1 second min between refreshes
2688
3690
  this.groupExpiryRefreshTriggered = false; // Track if expiry refresh already triggered for current group
3691
+ this.offlineRedemption = null;
3692
+ this.offlineRedemptionModal = null;
2689
3693
  /** Handle backend fulfillment notifications */
2690
3694
  this.handleGroupFulfilledEvent = (event) => {
2691
3695
  const detail = event.detail;
@@ -2842,11 +3846,22 @@ var CoBuySDK = (function (exports) {
2842
3846
  }
2843
3847
  /** Persist fulfilled state, emit callback, and refresh UI */
2844
3848
  processGroupFulfilled(eventData) {
2845
- var _a, _b;
3849
+ var _a, _b, _c, _d;
2846
3850
  this.groupFulfilled = true;
2847
- this.frozenReward = eventData.reward || null;
2848
- this.currentGroupId = eventData.groupId;
2849
- (_b = (_a = this.events) === null || _a === void 0 ? void 0 : _a.onGroupFulfilled) === null || _b === void 0 ? void 0 : _b.call(_a, eventData);
3851
+ // Extract reward from new event structure or fallback to legacy
3852
+ const reward = ((_a = eventData.frozen_reward) === null || _a === void 0 ? void 0 : _a.reward) || eventData.reward || null;
3853
+ this.frozenReward = reward;
3854
+ // Extract group ID from new event structure or fallback to legacy
3855
+ this.currentGroupId = ((_b = eventData.group) === null || _b === void 0 ? void 0 : _b.id) || eventData.groupId || null;
3856
+ // Store offline redemption data if available
3857
+ if (eventData.offline_redemption && isValidOfflineRedemption(eventData.offline_redemption)) {
3858
+ this.offlineRedemption = eventData.offline_redemption;
3859
+ this.logger.info("Offline redemption data stored", {
3860
+ member_id: this.offlineRedemption.member_id,
3861
+ code: this.offlineRedemption.redemption_code,
3862
+ });
3863
+ }
3864
+ (_d = (_c = this.events) === null || _c === void 0 ? void 0 : _c.onGroupFulfilled) === null || _d === void 0 ? void 0 : _d.call(_c, eventData);
2850
3865
  this.renderFulfilledState();
2851
3866
  }
2852
3867
  /** Re-render widget and external containers to reflect fulfillment */
@@ -2961,16 +3976,42 @@ var CoBuySDK = (function (exports) {
2961
3976
  rewardCheck.textContent = "✔";
2962
3977
  rewardCheck.style.color = "var(--cobuy-success-color, #10b981)";
2963
3978
  const rewardLabel = document.createElement("span");
2964
- rewardLabel.textContent = rewardText ? `Reward available: ${rewardText}` : "Reward available";
3979
+ rewardLabel.textContent = rewardText
3980
+ ? `Discount available: ${rewardText}`
3981
+ : "Discount available";
2965
3982
  rewardLine.appendChild(rewardCheck);
2966
3983
  rewardLine.appendChild(rewardLabel);
2967
3984
  root.appendChild(title);
2968
3985
  root.appendChild(rewardLine);
2969
- // Create footer with View all Groups link at bottom right
3986
+ // Create footer with action CTAs
2970
3987
  const footer = document.createElement("div");
2971
3988
  footer.style.display = "flex";
2972
3989
  footer.style.justifyContent = "flex-end";
2973
3990
  footer.style.alignItems = "center";
3991
+ footer.style.gap = "12px";
3992
+ // Add "Redeem In-store" CTA if offline redemption is available
3993
+ if (this.offlineRedemption && isValidOfflineRedemption(this.offlineRedemption)) {
3994
+ const redeemLink = document.createElement("button");
3995
+ redeemLink.className = "cobuy-redeem-instore-link";
3996
+ redeemLink.style.background = "none";
3997
+ redeemLink.style.border = "none";
3998
+ redeemLink.style.color = "var(--cobuy-primary-color, #3b82f6)";
3999
+ redeemLink.style.cursor = "pointer";
4000
+ redeemLink.style.fontSize = "12px";
4001
+ redeemLink.style.fontWeight = "600";
4002
+ redeemLink.style.textDecoration = "underline";
4003
+ redeemLink.style.padding = "0";
4004
+ redeemLink.style.transition = "opacity 0.2s";
4005
+ redeemLink.textContent = "Redeem In-store";
4006
+ redeemLink.addEventListener("click", () => this.openOfflineRedemptionModal());
4007
+ redeemLink.addEventListener("mouseover", () => {
4008
+ redeemLink.style.opacity = "0.7";
4009
+ });
4010
+ redeemLink.addEventListener("mouseout", () => {
4011
+ redeemLink.style.opacity = "1";
4012
+ });
4013
+ footer.appendChild(redeemLink);
4014
+ }
2974
4015
  root.appendChild(footer);
2975
4016
  return root;
2976
4017
  }
@@ -3154,6 +4195,12 @@ var CoBuySDK = (function (exports) {
3154
4195
  this.currentGroupData = groupData;
3155
4196
  this.currentGroupId = (groupData === null || groupData === void 0 ? void 0 : groupData.id) || null;
3156
4197
  this.currentRewardData = rewardData;
4198
+ console.log("group data for product ", options.productId, groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption, isValidOfflineRedemption(groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption));
4199
+ // Extract offline_redemption from API response if available
4200
+ if ((groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption) &&
4201
+ isValidOfflineRedemption(groupData.offline_redemption)) {
4202
+ this.offlineRedemption = groupData.offline_redemption;
4203
+ }
3157
4204
  // Check if group is already fulfilled (full) on initial load
3158
4205
  if (groupData) {
3159
4206
  const participants = Number(groupData.participants_count || 0);
@@ -3572,16 +4619,14 @@ var CoBuySDK = (function (exports) {
3572
4619
  // rewardText ? `Reward locked in: ${rewardText}` : "Reward available for this group",
3573
4620
  // );
3574
4621
  // rewardLine.title = rewardLine.textContent;
3575
- // }
3576
- // else if (activeReward) {
4622
+ // } else if (activeReward) {
3577
4623
  // const rewardText = this.formatRewardText(activeReward);
3578
4624
  // rewardLine.textContent = rewardText
3579
4625
  // ? `Save up to ${rewardText} with CoBuy`
3580
4626
  // : "CoBuy reward available";
3581
4627
  // rewardLine.setAttribute("aria-label", `Eligible for CoBuy reward: ${rewardLine.textContent}`);
3582
4628
  // rewardLine.title = rewardLine.textContent;
3583
- // }
3584
- // else {
4629
+ // } else {
3585
4630
  // rewardLine.textContent = "CoBuy offer loading or unavailable";
3586
4631
  // rewardLine.setAttribute("aria-label", "CoBuy offer loading or unavailable");
3587
4632
  // rewardLine.title = "CoBuy offer loading or unavailable";
@@ -3817,6 +4862,12 @@ var CoBuySDK = (function (exports) {
3817
4862
  }
3818
4863
  groupJoinData = joinResponse.data;
3819
4864
  this.logger.info("Successfully joined group", groupJoinData);
4865
+ this.offlineRedemption =
4866
+ groupJoinData &&
4867
+ groupJoinData.offline_redemption &&
4868
+ isValidOfflineRedemption(groupJoinData.offline_redemption)
4869
+ ? groupJoinData.offline_redemption
4870
+ : null;
3820
4871
  // Trigger invite tracking before opening lobby (global for product)
3821
4872
  try {
3822
4873
  const inviteResponse = await this.apiClient.inviteToGroup(this.currentGroupId, "copy_link");
@@ -3862,6 +4913,13 @@ var CoBuySDK = (function (exports) {
3862
4913
  const isGroupFulfilled = groupJoinData.group.participants_count >= groupJoinData.group.max_participants;
3863
4914
  const groupLinkFromInvite = inviteData === null || inviteData === void 0 ? void 0 : inviteData.invite_url;
3864
4915
  const shareMessageFromInvite = inviteData === null || inviteData === void 0 ? void 0 : inviteData.share_message;
4916
+ console.log("offlien dataaa", groupJoinData.offline_redemption, isValidOfflineRedemption(groupJoinData.offline_redemption));
4917
+ // Extract offline_redemption from join response if available
4918
+ const offlineRedemptionFromJoin = groupJoinData.offline_redemption &&
4919
+ isValidOfflineRedemption(groupJoinData.offline_redemption)
4920
+ ? groupJoinData.offline_redemption
4921
+ : undefined;
4922
+ this.offlineRedemption = offlineRedemptionFromJoin || null;
3865
4923
  // Prepare modal data with real values from join response
3866
4924
  cobuySDK.openModal({
3867
4925
  productId,
@@ -3880,6 +4938,7 @@ var CoBuySDK = (function (exports) {
3880
4938
  groupLink: groupLinkFromInvite || `https://cobuy.group/lobby/${groupJoinData.group.id}`,
3881
4939
  shareMessage: shareMessageFromInvite,
3882
4940
  isLocked: !isGroupFulfilled,
4941
+ offlineRedemption: offlineRedemptionFromJoin,
3883
4942
  activities: [
3884
4943
  {
3885
4944
  emoji: "👤",
@@ -3929,6 +4988,20 @@ var CoBuySDK = (function (exports) {
3929
4988
  }
3930
4989
  }
3931
4990
  }
4991
+ /**
4992
+ * Open offline redemption modal when user clicks "Redeem In-store"
4993
+ */
4994
+ openOfflineRedemptionModal() {
4995
+ if (!this.offlineRedemption || !isValidOfflineRedemption(this.offlineRedemption)) {
4996
+ this.logger.warn("Offline redemption data not available");
4997
+ return;
4998
+ }
4999
+ this.logger.info("Opening offline redemption modal");
5000
+ this.offlineRedemptionModal = new OfflineRedemptionModal(this.offlineRedemption, () => {
5001
+ this.offlineRedemptionModal = null;
5002
+ });
5003
+ this.offlineRedemptionModal.open();
5004
+ }
3932
5005
  /**
3933
5006
  * Emit checkout intent without performing navigation
3934
5007
  */
@@ -3987,13 +5060,13 @@ var CoBuySDK = (function (exports) {
3987
5060
  const reward = this.frozenReward || (rewardData === null || rewardData === void 0 ? void 0 : rewardData.reward);
3988
5061
  if (this.groupFulfilled) {
3989
5062
  const rewardText = this.formatRewardText(reward);
3990
- return rewardText ? `Reward available: ${rewardText}` : "Reward available for this group";
5063
+ return rewardText ? `Discount available: ${rewardText}` : "Discount available for this group";
3991
5064
  }
3992
5065
  if (!reward) {
3993
- return "Join with CoBuy to unlock rewards";
5066
+ return "Join with CoBuy to unlock discount";
3994
5067
  }
3995
5068
  const rewardText = this.formatRewardText(reward);
3996
- return rewardText ? `Save up to ${rewardText} with CoBuy` : "CoBuy reward available";
5069
+ return rewardText ? `Save up to ${rewardText} with CoBuy` : "CoBuy discount available";
3997
5070
  }
3998
5071
  /**
3999
5072
  * Public hook to request a realtime refresh from external callers
@@ -4591,7 +5664,7 @@ var CoBuySDK = (function (exports) {
4591
5664
  */
4592
5665
  async trackCtaClick(productId) {
4593
5666
  const event = {
4594
- event: "cta_clicked",
5667
+ event: "CTA_CLICKED",
4595
5668
  productId,
4596
5669
  timestamp: new Date().toISOString(),
4597
5670
  sessionId: this.sessionId,
@@ -4635,6 +5708,28 @@ var CoBuySDK = (function (exports) {
4635
5708
  throw error;
4636
5709
  }
4637
5710
  }
5711
+ /**
5712
+ * Track page view event
5713
+ */
5714
+ async trackPageView() {
5715
+ const event = {
5716
+ event: "PAGE_VIEW",
5717
+ timestamp: new Date().toISOString(),
5718
+ context: {
5719
+ pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
5720
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
5721
+ sdkVersion: this.sdkVersion,
5722
+ },
5723
+ };
5724
+ try {
5725
+ await this.sendEvent(event);
5726
+ this.logger.info("[Analytics] Page view tracked");
5727
+ }
5728
+ catch (error) {
5729
+ // Log but don't throw - analytics failure should not break UX
5730
+ this.logger.error("[Analytics] Failed to track page view", error);
5731
+ }
5732
+ }
4638
5733
  /**
4639
5734
  * Track custom event (extensible for future use)
4640
5735
  */
@@ -8822,9 +9917,10 @@ var CoBuySDK = (function (exports) {
8822
9917
  * Manages a single socket.io connection for the SDK
8823
9918
  */
8824
9919
  class SocketManager {
8825
- constructor(config, sdkVersion) {
9920
+ constructor(config, sdkVersion, sessionId) {
8826
9921
  this.config = config;
8827
9922
  this.sdkVersion = sdkVersion;
9923
+ this.sessionId = sessionId;
8828
9924
  this.socket = null;
8829
9925
  this.logger = new Logger(config.debug);
8830
9926
  }
@@ -8869,6 +9965,7 @@ var CoBuySDK = (function (exports) {
8869
9965
  auth: {
8870
9966
  merchantKey: this.config.merchantKey,
8871
9967
  sdkVersion: this.sdkVersion,
9968
+ sessionId: this.sessionId,
8872
9969
  },
8873
9970
  autoConnect: true,
8874
9971
  });
@@ -9055,7 +10152,9 @@ var CoBuySDK = (function (exports) {
9055
10152
  const ref = window.localStorage.getItem(key);
9056
10153
  if (ref) {
9057
10154
  this.logger.debug(`[SDK] Retrieved checkout reference via prefix: ${key}`);
9058
- const parsedGroupId = key.startsWith(`${basePrefix}_`) ? key.substring(basePrefix.length + 1) : null;
10155
+ const parsedGroupId = key.startsWith(`${basePrefix}_`)
10156
+ ? key.substring(basePrefix.length + 1)
10157
+ : null;
9059
10158
  return { key, checkoutRef: ref, groupId: parsedGroupId };
9060
10159
  }
9061
10160
  }
@@ -9145,7 +10244,7 @@ var CoBuySDK = (function (exports) {
9145
10244
  // Initialize real-time socket connection for public mode
9146
10245
  if (config.authMode === "public" && config.merchantKey) {
9147
10246
  try {
9148
- this.socketManager = new SocketManager(config, SDK_VERSION);
10247
+ this.socketManager = new SocketManager(config, SDK_VERSION, this.sessionId);
9149
10248
  this.socketManager.connect();
9150
10249
  this.socketManager.bindHandlers({
9151
10250
  onGroupFulfilled: (payload) => {
@@ -9187,6 +10286,13 @@ var CoBuySDK = (function (exports) {
9187
10286
  this.logger.warn("[SDK] Failed to initialize sockets", e);
9188
10287
  }
9189
10288
  }
10289
+ // Track page view event after successful initialization
10290
+ if (this.analyticsClient) {
10291
+ this.analyticsClient.trackPageView().catch((error) => {
10292
+ // Non-blocking: Analytics failure should not affect SDK initialization
10293
+ this.logger.warn("[SDK] Failed to track page view", error);
10294
+ });
10295
+ }
9190
10296
  this.logger.info("SDK initialization complete");
9191
10297
  }
9192
10298
  catch (error) {
@@ -9266,6 +10372,7 @@ var CoBuySDK = (function (exports) {
9266
10372
  groupLink: options.groupLink,
9267
10373
  activities: options.activities,
9268
10374
  isLocked: options.isLocked,
10375
+ offlineRedemption: options.offlineRedemption,
9269
10376
  };
9270
10377
  // Create modal instance
9271
10378
  const modal = new LobbyModal(modalData, {