diff --git a/assets/js/chat.js b/assets/js/chat.js new file mode 100644 index 0000000..a5a611f --- /dev/null +++ b/assets/js/chat.js @@ -0,0 +1,565 @@ +/** + * ============================================================================== + * SUPPORT WIDGET — Web Component (Shadow DOM) + * ============================================================================== + * Shadow DOM cô lập CSS hoàn toàn, tránh conflict/flicker với host page. + * 3 tabs: SMS (AU Only) / Email / WhatsApp + * ============================================================================== + */ +(function (w, d) { + "use strict"; + + if (w.__MSW_LOADED__) return; + w.__MSW_LOADED__ = true; + + var P = w.PROLOGY_CONFIG || {}; + var CFG = { + apiEndpoint: + P.CHAT_API_URL || + P.API_URL || + "https://prologyms.nswteam.net/chat-plugin/api/support", + whatsappNumber: P.CHAT_WHATSAPP_NUMBER || "84901234567", + accentColor: P.CHAT_ACCENT_COLOR || "#4f46e5", + position: P.CHAT_POSITION || "right", + rateLimit: { + max: P.CHAT_RATE_LIMIT_MAX != null ? P.CHAT_RATE_LIMIT_MAX : 3, + windowMin: + P.CHAT_RATE_LIMIT_WINDOW_MIN != null ? P.CHAT_RATE_LIMIT_WINDOW_MIN : 10, + }, + minFillMs: P.CHAT_MIN_FILL_MS != null ? P.CHAT_MIN_FILL_MS : 3000, + fabImageUrl: + P.CHAT_FAB_IMAGE_URL || + "https://prology.nswteam.net/media/wysiwyg/image_2026-04-02_15-43-14.png", + }; + + /* ── Utilities ── */ + function sanitize(str, maxLen) { + if (typeof str !== "string") return ""; + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/\//g, "/") + .trim() + .slice(0, maxLen || 2000); + } + function nonce() { + var a = new Uint8Array(16); + (w.crypto || w.msCrypto).getRandomValues(a); + return Array.from(a, function (b) { + return ("0" + b.toString(16)).slice(-2); + }).join(""); + } + var RL_KEY = "_msw_rl"; + function getRl() { + try { + return ( + JSON.parse(sessionStorage.getItem(RL_KEY)) || { n: 0, t: Date.now() } + ); + } catch (e) { + return { n: 0, t: Date.now() }; + } + } + function setRl(o) { + try { + sessionStorage.setItem(RL_KEY, JSON.stringify(o)); + } catch (e) {} + } + function isLimited() { + var cfg = CFG.rateLimit, + rl = getRl(), + win = cfg.windowMin * 60 * 1000; + if (Date.now() - rl.t > win) { + setRl({ n: 0, t: Date.now() }); + return false; + } + return rl.n >= cfg.max; + } + function limitCooldown() { + var rl = getRl(), + win = CFG.rateLimit.windowMin * 60 * 1000; + return Math.max(0, Math.ceil((rl.t + win - Date.now()) / 1000)); + } + function bumpRl() { + var rl = getRl(); + rl.n++; + setRl(rl); + } + function csrfToken() { + var el = d.querySelector('input[name="form_key"]'); + return el ? el.value : ""; + } + function validateSMS(f) { + var e = []; + var name = (f.name || "").trim(); + var phone = (f.phone || "").trim(); + var message = (f.message || "").trim(); + if (!name || !/^[\p{L}\p{M}'\-\s]{2,80}$/u.test(name)) e.push("sms-name"); + if ( + !phone || + !/^[\d\s\+\-\(\)]{7,15}$/.test(phone) || + !/\d{5,}/.test(phone) + ) + e.push("sms-phone"); + if (!message || message.length < 10) e.push("sms-msg"); + return e; + } + function validateEmail(f) { + var e = []; + var name = (f.name || "").trim(); + var email = (f.email || "").trim(); + var message = (f.message || "").trim(); + if (!name || !/^[\p{L}\p{M}'\-\s]{2,80}$/u.test(name)) e.push("email-name"); + if (!email || !/^\w+([.\-]\w+)*@([\w\-]+\.)+[a-zA-Z]{2,12}$/.test(email)) + e.push("email-email"); + if (!message || message.length < 10) e.push("email-msg"); + return e; + } + + /* ── SVG assets ── */ + var SVG_ARROW = + ''; + var WA_PATH = + "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347zM11.979 0C5.37 0 0 5.373 0 11.979c0 2.11.553 4.094 1.518 5.818L.057 24l6.349-1.663A11.938 11.938 0 0011.979 24C18.588 24 24 18.626 24 11.979 24 5.373 18.588 0 11.979 0zm0 21.818a9.839 9.839 0 01-5.012-1.369l-.36-.214-3.73.978.995-3.636-.235-.374a9.806 9.806 0 01-1.506-5.224c0-5.42 4.413-9.833 9.848-9.833 5.437 0 9.851 4.413 9.851 9.833 0 5.421-4.414 9.839-9.851 9.839z"; + function waSVG(size) { + return ( + '' + ); + } + var SVG_CHECK = + ''; + + /* ── Shadow CSS ── */ + var ac = CFG.accentColor; + var pos = CFG.position === "left" ? "left:24px;" : "right:24px;"; + + var SHADOW_CSS = [ + ':host{all:initial;display:block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;}', + /* FAB — no transition on background/box-shadow to kill flicker; only transform transitions */ + "#fab{position:fixed;bottom:24px;" + pos + "z-index:99998;", + "width:60px;height:60px;border-radius:50%;", + "background-image:url(" + CFG.fabImageUrl + ");", + "background-size:cover;background-position:center;background-repeat:no-repeat;", + "border:none;cursor:pointer;outline:none;", + "box-shadow:0 6px 20px rgba(79,70,229,.4);", + "transform:translateZ(0);transition:transform .22s cubic-bezier(.34,1.56,.64,1);}", + "#fab:hover{transform:scale(1.08) translateY(-3px) translateZ(0);}", + "#badge{position:absolute;top:-4px;right:-4px;width:20px;height:20px;border-radius:50%;", + "background:#ef4444;font-size:11px;font-weight:700;color:#fff;", + "display:none;align-items:center;justify-content:center;border:2px solid #1a1f2e;}", + /* Panel */ + "#panel{position:fixed;bottom:92px;" + pos + "z-index:99999;width:360px;", + "background:linear-gradient(145deg,rgb(42,59,84),rgb(30,41,59));border-radius:18px;overflow:hidden;", + "box-shadow:0 20px 60px rgba(0,0,0,.55),0 4px 16px rgba(0,0,0,.3);", + "transform:scale(.9) translateY(16px) translateZ(0);opacity:0;pointer-events:none;", + "transition:transform .28s cubic-bezier(.34,1.56,.64,1),opacity .2s ease;", + "will-change:transform,opacity;}", + "#panel.open{transform:scale(1) translateY(0) translateZ(0);opacity:1;pointer-events:auto;}", + /* Tabs */ + "#tabs{display:flex;gap:3px;background:#242938;margin:14px 14px 0;border-radius:11px;padding:4px;}", + ".tab{flex:1;padding:8px 4px;font-size:11.5px;font-weight:700;text-align:center;line-height:1.35;", + "color:#8b92a8;background:transparent;border:none;cursor:pointer;border-radius:8px;", + "transition:background .18s,color .18s;font-family:inherit;letter-spacing:.3px;}", + ".tab:hover{color:#e0e2ea;}", + ".tab.active{background:" + ac + ";color:#fff;}", + /* Panes */ + ".pane{display:none;padding:14px 14px 18px;}", + ".pane.active{display:block;}", + /* Fields */ + ".field{margin-bottom:11px;}", + ".field input,.field textarea{width:100%;padding:11px 13px;font-size:14px;font-family:inherit;", + "background:#252b3b;color:#dde0eb;border:1px solid #333b52;border-radius:9px;outline:none;", + "transition:border-color .18s,box-shadow .18s;box-sizing:border-box;-webkit-appearance:none;}", + ".field input::placeholder,.field textarea::placeholder{color:#5d6478;}", + ".field input:focus,.field textarea:focus{border-color:" + + ac + + ";box-shadow:0 0 0 3px rgba(79,70,229,.22);}", + ".field input.err,.field textarea.err{border-color:#ef4444;}", + ".field textarea{resize:vertical;min-height:88px;}", + ".hint{font-size:11.5px;color:#f87171;margin-top:4px;display:none;}", + ".hint.show{display:block;}", + ".hp{position:absolute;left:-9999px;opacity:0;height:0;width:0;overflow:hidden;pointer-events:none;}", + /* Submit */ + ".btn-submit{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px;", + "background:" + ac + ";color:#fff;border:none;border-radius:9px;", + "font-size:14.5px;font-weight:600;font-family:inherit;cursor:pointer;margin-top:6px;", + "transition:background .18s;}", + ".btn-submit:hover{background:#4338ca;}", + ".btn-submit:active{transform:scale(.98);}", + ".btn-submit:disabled{background:#4a4f61;cursor:not-allowed;}", + /* Status */ + ".status{font-size:13px;font-weight:500;margin-top:9px;text-align:center;", + "border-radius:7px;padding:8px 12px;display:none;}", + ".status.ok{display:block;background:#0a2e1a;color:#34d399;}", + ".status.fail{display:block;background:#2e0a0a;color:#f87171;}", + ".status.warn{display:block;background:#2c1f00;color:#fbbf24;}", + /* WhatsApp pane */ + ".wa-wrap{text-align:center;padding:4px 0 2px;}", + ".wa-icon-wrap{width:54px;height:54px;border-radius:50%;background:#1a2e20;", + "display:flex;align-items:center;justify-content:center;margin:0 auto 14px;color:#25d366;}", + ".wa-desc{font-size:13.5px;color:#8b92a8;line-height:1.6;margin:0 0 18px;}", + ".btn-wa{display:flex;align-items:center;justify-content:center;gap:9px;width:100%;padding:13px;", + "background:#25d366;color:#fff;border:none;border-radius:9px;", + "font-size:14.5px;font-weight:600;font-family:inherit;cursor:pointer;transition:background .18s;}", + ".btn-wa:hover{background:#1db954;}", + /* Success */ + "#success{display:none;flex-direction:column;align-items:center;text-align:center;padding:36px 20px;}", + ".success-icon{width:58px;height:58px;border-radius:50%;background:#0a2e1a;", + "display:flex;align-items:center;justify-content:center;color:#34d399;margin-bottom:15px;}", + "#success h3{font-size:18px;font-weight:700;color:#dde0eb;margin:0 0 8px;}", + "#success p{font-size:13.5px;color:#8b92a8;margin:0 0 22px;line-height:1.55;}", + ".btn-new{padding:10px 24px;font-size:13.5px;font-weight:600;color:" + + ac + + ";", + "background:rgba(79,70,229,.13);border:none;border-radius:8px;cursor:pointer;", + "transition:background .18s;font-family:inherit;}", + ".btn-new:hover{background:rgba(79,70,229,.22);}", + /* Responsive */ + "@media(max-width:420px){#panel{width:calc(100vw - 28px);" + + (CFG.position === "left" ? "left:14px;" : "right:14px;") + + "}", + "#fab{bottom:20px;}#panel{bottom:88px;}}", + ].join(""); + + /* ── Inner HTML ── */ + var INNER_HTML = [ + '', + '
Chat with us on WhatsApp for support,
inquiries, and immediate expert advice.
Thanks for reaching out.
We'll get back to you shortly.