@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.
- package/dist/cobuy-sdk.esm.js +1185 -78
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +1185 -78
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/types/core/analytics.d.ts +6 -2
- package/dist/types/core/group-channel-manager.d.ts +74 -0
- package/dist/types/core/offline-redemption.d.ts +41 -0
- package/dist/types/core/socket.d.ts +2 -1
- package/dist/types/core/types.d.ts +54 -4
- package/dist/types/ui/group-list/group-list-modal.d.ts +1 -0
- package/dist/types/ui/lobby/lobby-modal.d.ts +39 -1
- package/dist/types/ui/offline-redemption/offline-redemption-modal.d.ts +35 -0
- package/dist/types/ui/widget/widget-root.d.ts +6 -0
- package/package.json +1 -1
- package/dist/types/core/socket-client.d.ts +0 -126
package/dist/cobuy-sdk.umd.js
CHANGED
|
@@ -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
|
|
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: "
|
|
1555
|
-
subtitle: "Group filled — enjoy your
|
|
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
|
|
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
|
|
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
|
|
1640
|
-
|
|
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
|
-
//
|
|
1684
|
-
const
|
|
1685
|
-
leftWp.appendChild(
|
|
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-
|
|
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
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
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" ? "
|
|
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
|
-
|
|
1713
|
-
|
|
1714
|
-
statusWp.appendChild(
|
|
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
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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
|
|
1740
|
-
|
|
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("
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
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
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
this.socketManager.
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
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
|
|
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
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
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
|
|
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
|
|
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 ? `
|
|
5063
|
+
return rewardText ? `Discount available: ${rewardText}` : "Discount available for this group";
|
|
3991
5064
|
}
|
|
3992
5065
|
if (!reward) {
|
|
3993
|
-
return "Join with CoBuy to unlock
|
|
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
|
|
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: "
|
|
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}_`)
|
|
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, {
|