copilot-chat-widget 0.1.32 → 0.1.33

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/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const k="http://localhost:3000",w=64,C=720,E=({iframeUrl:h,launcherIcon:u})=>{const d=document.querySelector("[data-copilot-widget-root]");d&&d.remove();const g=s=>{const o=document.querySelector("[data-copilot-checkout-toast]");o&&o.remove();const r=document.createElement("div");r.setAttribute("data-copilot-checkout-toast","true"),r.innerText=s,Object.assign(r.style,{position:"fixed",top:"20px",left:"50%",transform:"translateX(-50%)",padding:"12px 16px",background:"#0f172a",color:"white",borderRadius:"12px",boxShadow:"0 8px 24px rgba(0,0,0,0.25)",zIndex:2147483647,fontSize:"14px",fontWeight:"600",maxWidth:"420px",lineHeight:"1.4",textAlign:"center"}),document.body.appendChild(r),setTimeout(()=>{r.remove()},4500)},c=document.createElement("div");c.setAttribute("data-copilot-widget-root","true");const n=c.attachShadow({mode:"open"});document.body.appendChild(c);const t=document.createElement("button");t.type="button",t.setAttribute("aria-label","Open Copilot chat"),t.innerHTML=`
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const N="http://localhost:3000",k=64,T=720,$=({iframeUrl:f,launcherIcon:x})=>{const u=document.querySelector("[data-copilot-widget-root]");u&&u.remove();const y=t=>{const e=document.querySelector("[data-copilot-checkout-toast]");e&&e.remove();const n=document.createElement("div");n.setAttribute("data-copilot-checkout-toast","true"),n.innerText=t,Object.assign(n.style,{position:"fixed",top:"20px",left:"50%",transform:"translateX(-50%)",padding:"12px 16px",background:"#0f172a",color:"white",borderRadius:"12px",boxShadow:"0 8px 24px rgba(0,0,0,0.25)",zIndex:2147483647,fontSize:"14px",fontWeight:"600",maxWidth:"420px",lineHeight:"1.4",textAlign:"center"}),document.body.appendChild(n),setTimeout(()=>{n.remove()},4500)},w=document.createElement("div");w.setAttribute("data-copilot-widget-root","true");const a=w.attachShadow({mode:"open"});document.body.appendChild(w);const o=document.createElement("button");o.type="button",o.setAttribute("aria-label","Open Copilot chat"),o.innerHTML=`
2
2
  <img
3
- src="${u}"
3
+ src="${x}"
4
4
  alt="Copilot chat launcher"
5
5
  style="width: 38px; height: 38px; object-fit: contain; border-radius: 50%; pointer-events: none;"
6
6
  />
7
- `,Object.assign(t.style,{position:"fixed",bottom:"24px",right:"24px",width:`${w}px`,height:`${w}px`,borderRadius:"50%",border:"none",background:"linear-gradient(135deg, #0078ff, #00c6ff)",color:"white",cursor:"pointer",zIndex:999998,display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 6px 14px rgba(0,0,0,0.25)",transition:"all 0.25s ease"}),t.onmouseover=()=>{t.style.transform="scale(1.12)",t.style.boxShadow="0 10px 25px rgba(0,0,0,0.3)"},t.onmouseout=()=>{t.style.transform="scale(1)",t.style.boxShadow="0 6px 14px rgba(0,0,0,0.25)"};const e=document.createElement("div");e.setAttribute("data-copilot-widget-root","true"),Object.assign(e.style,{display:"none",position:"fixed",bottom:`${w+36}px`,right:"24px",zIndex:"999999",transformOrigin:"bottom right",transform:"scale(0.8) translateY(20px)",opacity:"0",transition:"all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)"});const l=document.createElement("div"),f=document.createElement("div"),x=document.createElement("div");Object.assign(l.style,{position:"absolute",bottom:"-14px",right:"28px",width:"30px",height:"20px",pointerEvents:"none",display:"none",zIndex:"1"}),Object.assign(f.style,{position:"absolute",bottom:"0",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"15px solid transparent",borderRight:"15px solid transparent",borderTop:"15px solid rgba(15,23,42,0.1)"}),Object.assign(x.style,{position:"absolute",bottom:"2px",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"13px solid transparent",borderRight:"13px solid transparent",borderTop:"13px solid white",boxShadow:"0 6px 16px rgba(15,23,42,0.12)",borderRadius:"2px"}),l.appendChild(f),l.appendChild(x);const p=document.createElement("div");Object.assign(p.style,{width:`${C}px`,maxWidth:"calc(100vw - 48px)",height:`${Math.min(C,Math.max(320,window.innerHeight-140))}px`,maxHeight:"calc(100vh - 150px)",borderRadius:"20px",background:"white",border:"1px solid rgba(15,23,42,0.12)",boxShadow:"0 18px 45px rgba(15,23,42,0.16)",overflow:"hidden"}),n.appendChild(p);const i=document.createElement("iframe");i.src=h,i.title="Copilot chat widget",i.allow="clipboard-read; clipboard-write; microphone; camera; display-capture",i.setAttribute("scrolling","no"),Object.assign(i.style,{width:"100%",height:"100%",border:"none",display:"block",background:"transparent",overflow:"hidden"}),n.appendChild(p),p.appendChild(i),e.appendChild(l),e.appendChild(p),document.body.appendChild(t),document.body.appendChild(e);let a=!1;const m=()=>{a&&(a=!1,e.style.opacity="0",e.style.transform="scale(0.8) translateY(20px)",l.style.display="none",setTimeout(()=>{e.style.display="none"},250),t.style.transform="scale(1)")},v=s=>{const{data:o,source:r}=s||{};if(o?.type){if(o.type==="CART_CHECKOUT"&&r===i.contentWindow){console.log("[CopilotChat] Received checkout payload from widget:",o),g("Checkout message received from chat widget. Check console for payload.");return}if(o.type==="WIDGET_READY"&&r===i.contentWindow){i.contentWindow.postMessage({type:"INIT_WIDGET"},"*");return}o.type==="CHAT_CLOSED"&&m()}};window.addEventListener("message",v),t.onclick=s=>{s.stopPropagation(),a=!a,a?(e.style.display="block",l.style.display="block",requestAnimationFrame(()=>{e.style.opacity="1",e.style.transform="scale(1) translateY(0)"})):m()},document.addEventListener("click",s=>{const o=s.target;o&&a&&!e.contains(o)&&o!==t&&m()});const b={close:m,open:()=>{a||t.click()}};return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.close=b.close,window.CopilotChat.open=b.open,window.CopilotChat.controls=b,b};async function y(h={}){if(typeof window>"u"||typeof document>"u")return null;const u=window.CopilotChatConfig||{},d=h.token||u.token||typeof window<"u"&&window.localStorage?.getItem("copilotChatToken")||null;if(!d)return console.error("[CopilotChat] Missing token (provide via loadCopilotChatWidget({ token }) or window.CopilotChatConfig.token)."),null;const g=h.baseUrl||u.baseUrl||k,c=`${g.replace(/\/$/,"")}/api/chat-widget/config?token=${encodeURIComponent(d)}`;try{const n=await fetch(c,{credentials:"omit",mode:"cors"});if(!n.ok)throw new Error(`Server responded with ${n.status}`);const t=await n.json();if(!t?.iframeUrl||!t?.launcherIcon)throw new Error("Received incomplete widget configuration from server.");return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.token=d,window.CopilotChat.baseUrl=g,E({iframeUrl:t.iframeUrl,launcherIcon:t.launcherIcon})}catch(n){return console.error("[CopilotChat] Error during widget bootstrap:",n instanceof Error?n.message:n),null}}exports.default=y;exports.loadCopilotChatWidget=y;
7
+ `,Object.assign(o.style,{position:"fixed",bottom:"24px",right:"24px",width:`${k}px`,height:`${k}px`,borderRadius:"50%",border:"none",background:"linear-gradient(135deg, #0078ff, #00c6ff)",color:"white",cursor:"pointer",zIndex:999998,display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 6px 14px rgba(0,0,0,0.25)",transition:"all 0.25s ease"}),o.onmouseover=()=>{o.style.transform="scale(1.12)",o.style.boxShadow="0 10px 25px rgba(0,0,0,0.3)"},o.onmouseout=()=>{o.style.transform="scale(1)",o.style.boxShadow="0 6px 14px rgba(0,0,0,0.25)"};const i=document.createElement("div");i.setAttribute("data-copilot-widget-root","true"),Object.assign(i.style,{display:"none",position:"fixed",bottom:`${k+36}px`,right:"24px",zIndex:"999999",transformOrigin:"bottom right",transform:"scale(0.8) translateY(20px)",opacity:"0",transition:"all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)"});const g=document.createElement("div"),E=document.createElement("div"),v=document.createElement("div");Object.assign(g.style,{position:"absolute",bottom:"-14px",right:"28px",width:"30px",height:"20px",pointerEvents:"none",display:"none",zIndex:"1"}),Object.assign(E.style,{position:"absolute",bottom:"0",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"15px solid transparent",borderRight:"15px solid transparent",borderTop:"15px solid rgba(15,23,42,0.1)"}),Object.assign(v.style,{position:"absolute",bottom:"2px",left:"0",right:"0",margin:"0 auto",width:"0",height:"0",borderLeft:"13px solid transparent",borderRight:"13px solid transparent",borderTop:"13px solid white",boxShadow:"0 6px 16px rgba(15,23,42,0.12)",borderRadius:"2px"}),g.appendChild(E),g.appendChild(v);const b=document.createElement("div");Object.assign(b.style,{width:`${T}px`,maxWidth:"calc(100vw - 48px)",height:`${Math.min(T,Math.max(320,window.innerHeight-140))}px`,maxHeight:"calc(100vh - 150px)",borderRadius:"20px",background:"white",border:"1px solid rgba(15,23,42,0.12)",boxShadow:"0 18px 45px rgba(15,23,42,0.16)",overflow:"hidden"}),a.appendChild(b);const r=document.createElement("iframe");r.src=f,r.title="Copilot chat widget",r.allow="clipboard-read; clipboard-write; microphone; camera; display-capture",r.setAttribute("scrolling","no"),Object.assign(r.style,{width:"100%",height:"100%",border:"none",display:"block",background:"transparent",overflow:"hidden"}),a.appendChild(b),b.appendChild(r),i.appendChild(g),i.appendChild(b),document.body.appendChild(o),document.body.appendChild(i);let d=!1;const C=()=>{d&&(d=!1,i.style.opacity="0",i.style.transform="scale(0.8) translateY(20px)",g.style.display="none",setTimeout(()=>{i.style.display="none"},250),o.style.transform="scale(1)")},P=(t,e=null)=>{if(!t)return e;try{return JSON.parse(t)}catch{return e}},A=t=>String(t).toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,""),s=(t,e)=>({value:t,currency:e,__typename:"Money"}),R=(t,e)=>({maximum_price:{discount:{amount_off:0,percent_off:0,__typename:"ProductDiscount"},final_price:s(t,e),fixed_product_taxes:[],regular_price:s(t,e),__typename:"ProductPrice"},minimum_price:{discount:{amount_off:0,percent_off:0,__typename:"ProductDiscount"},final_price:s(t,e),fixed_product_taxes:[],regular_price:s(t,e),__typename:"ProductPrice"},__typename:"PriceRange"}),W=(t,e)=>{const n=Number(t?.quantity||0),m=Number(t?.price||t?.unitPrice||0),c=m*n,p=t?.name||"Chat widget product",S=t?.image||t?.thumbnail||"",h=Number(t?.productId||t?.id||0),l=String(t?.sku||t?.id||h||"chat-widget");return{id:String(t?.id||l||h||Date.now()),quantity:n,product:{id:h||Date.now(),sku:l,name:p,url_key:t?.url_key||A(p),url_suffix:".html",stock_status:"IN_STOCK",brand:null,has_gift:null,price_range:R(m,e),thumbnail:{disabled:null,label:p,position:null,url:S,__typename:"ProductImage"},small_image:{disabled:null,label:p,position:null,url:S,__typename:"ProductImage"},categories:[],special_from_date:null,special_price:null,special_to_date:null,__typename:"SimpleProduct"},prices:{discounts:null,price:s(m,e),row_total:s(c,e),row_total_including_tax:s(c,e),total_item_discount:s(0,e),__typename:"CartItemPrices"},__typename:"SimpleCartItem",itemTotal:c}},U=t=>{const e=P(window.localStorage.getItem("web-cart")),n=Array.isArray(t?.cart)?t.cart:[],m=t?.currency||"VND",c=n.map(l=>W(l,m)),p=c.reduce((l,I)=>l+(I.quantity||0),0),S=Number(t?.totalPrice||0)||c.reduce((l,I)=>l+(I.itemTotal||0),0),h={totalItems:p,total:s(S,m),id:e?.id||`chat-widget-${Date.now()}`,is_virtual:!1,shipping_addresses:Array.isArray(e?.shipping_addresses)?e.shipping_addresses:[],total_quantity:p,items:c,isCartIdLoading:!1,isFetchingCart:!1};window.localStorage.setItem("web-cart",JSON.stringify(h)),window.dispatchEvent(new StorageEvent("storage",{key:"web-cart",newValue:JSON.stringify(h)}))},j=t=>{const{data:e,source:n}=t||{};if(e?.type){if(e.type==="CART_CHECKOUT"&&n===r.contentWindow){console.log("[CopilotChat] Received checkout payload from widget:",e),U(e?.data),y("Cart updated on KidsPlaza demo. Open cart to see changes.");return}if(e.type==="WIDGET_READY"&&n===r.contentWindow){r.contentWindow.postMessage({type:"INIT_WIDGET"},"*");return}e.type==="CHAT_CLOSED"&&C()}};window.addEventListener("message",j),o.onclick=t=>{t.stopPropagation(),d=!d,d?(i.style.display="block",g.style.display="block",requestAnimationFrame(()=>{i.style.opacity="1",i.style.transform="scale(1) translateY(0)"})):C()},document.addEventListener("click",t=>{const e=t.target;e&&d&&!i.contains(e)&&e!==o&&C()});const _={close:C,open:()=>{d||o.click()}};return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.close=_.close,window.CopilotChat.open=_.open,window.CopilotChat.controls=_,_};async function O(f={}){if(typeof window>"u"||typeof document>"u")return null;const x=window.CopilotChatConfig||{},u=f.token||x.token||typeof window<"u"&&window.localStorage?.getItem("copilotChatToken")||null;if(!u)return console.error("[CopilotChat] Missing token (provide via loadCopilotChatWidget({ token }) or window.CopilotChatConfig.token)."),null;const y=f.baseUrl||x.baseUrl||N,w=`${y.replace(/\/$/,"")}/api/chat-widget/config?token=${encodeURIComponent(u)}`;try{const a=await fetch(w,{credentials:"omit",mode:"cors"});if(!a.ok)throw new Error(`Server responded with ${a.status}`);const o=await a.json();if(!o?.iframeUrl||!o?.launcherIcon)throw new Error("Received incomplete widget configuration from server.");return window.CopilotChat=window.CopilotChat||{},window.CopilotChat.token=u,window.CopilotChat.baseUrl=y,$({iframeUrl:o.iframeUrl,launcherIcon:o.launcherIcon})}catch(a){return console.error("[CopilotChat] Error during widget bootstrap:",a instanceof Error?a.message:a),null}}exports.default=O;exports.loadCopilotChatWidget=O;
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
- const C = "http://localhost:3000";
2
- const y = ({ iframeUrl: h, launcherIcon: u }) => {
3
- const d = document.querySelector("[data-copilot-widget-root]");
4
- d && d.remove();
5
- const g = (s) => {
6
- const o = document.querySelector("[data-copilot-checkout-toast]");
7
- o && o.remove();
8
- const r = document.createElement("div");
9
- r.setAttribute("data-copilot-checkout-toast", "true"), r.innerText = s, Object.assign(r.style, {
1
+ const U = "http://localhost:3000";
2
+ const N = ({ iframeUrl: b, launcherIcon: x }) => {
3
+ const u = document.querySelector("[data-copilot-widget-root]");
4
+ u && u.remove();
5
+ const y = (t) => {
6
+ const e = document.querySelector("[data-copilot-checkout-toast]");
7
+ e && e.remove();
8
+ const n = document.createElement("div");
9
+ n.setAttribute("data-copilot-checkout-toast", "true"), n.innerText = t, Object.assign(n.style, {
10
10
  position: "fixed",
11
11
  top: "20px",
12
12
  left: "50%",
@@ -22,21 +22,21 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
22
22
  maxWidth: "420px",
23
23
  lineHeight: "1.4",
24
24
  textAlign: "center"
25
- }), document.body.appendChild(r), setTimeout(() => {
26
- r.remove();
25
+ }), document.body.appendChild(n), setTimeout(() => {
26
+ n.remove();
27
27
  }, 4500);
28
- }, c = document.createElement("div");
29
- c.setAttribute("data-copilot-widget-root", "true");
30
- const n = c.attachShadow({ mode: "open" });
31
- document.body.appendChild(c);
32
- const t = document.createElement("button");
33
- t.type = "button", t.setAttribute("aria-label", "Open Copilot chat"), t.innerHTML = `
28
+ }, w = document.createElement("div");
29
+ w.setAttribute("data-copilot-widget-root", "true");
30
+ const a = w.attachShadow({ mode: "open" });
31
+ document.body.appendChild(w);
32
+ const o = document.createElement("button");
33
+ o.type = "button", o.setAttribute("aria-label", "Open Copilot chat"), o.innerHTML = `
34
34
  <img
35
- src="${u}"
35
+ src="${x}"
36
36
  alt="Copilot chat launcher"
37
37
  style="width: 38px; height: 38px; object-fit: contain; border-radius: 50%; pointer-events: none;"
38
38
  />
39
- `, Object.assign(t.style, {
39
+ `, Object.assign(o.style, {
40
40
  position: "fixed",
41
41
  bottom: "24px",
42
42
  right: "24px",
@@ -53,13 +53,13 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
53
53
  justifyContent: "center",
54
54
  boxShadow: "0 6px 14px rgba(0,0,0,0.25)",
55
55
  transition: "all 0.25s ease"
56
- }), t.onmouseover = () => {
57
- t.style.transform = "scale(1.12)", t.style.boxShadow = "0 10px 25px rgba(0,0,0,0.3)";
58
- }, t.onmouseout = () => {
59
- t.style.transform = "scale(1)", t.style.boxShadow = "0 6px 14px rgba(0,0,0,0.25)";
56
+ }), o.onmouseover = () => {
57
+ o.style.transform = "scale(1.12)", o.style.boxShadow = "0 10px 25px rgba(0,0,0,0.3)";
58
+ }, o.onmouseout = () => {
59
+ o.style.transform = "scale(1)", o.style.boxShadow = "0 6px 14px rgba(0,0,0,0.25)";
60
60
  };
61
- const e = document.createElement("div");
62
- e.setAttribute("data-copilot-widget-root", "true"), Object.assign(e.style, {
61
+ const i = document.createElement("div");
62
+ i.setAttribute("data-copilot-widget-root", "true"), Object.assign(i.style, {
63
63
  display: "none",
64
64
  position: "fixed",
65
65
  bottom: "100px",
@@ -70,8 +70,8 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
70
70
  opacity: "0",
71
71
  transition: "all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)"
72
72
  });
73
- const l = document.createElement("div"), b = document.createElement("div"), f = document.createElement("div");
74
- Object.assign(l.style, {
73
+ const m = document.createElement("div"), E = document.createElement("div"), k = document.createElement("div");
74
+ Object.assign(m.style, {
75
75
  position: "absolute",
76
76
  bottom: "-14px",
77
77
  right: "28px",
@@ -80,7 +80,7 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
80
80
  pointerEvents: "none",
81
81
  display: "none",
82
82
  zIndex: "1"
83
- }), Object.assign(b.style, {
83
+ }), Object.assign(E.style, {
84
84
  position: "absolute",
85
85
  bottom: "0",
86
86
  left: "0",
@@ -91,7 +91,7 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
91
91
  borderLeft: "15px solid transparent",
92
92
  borderRight: "15px solid transparent",
93
93
  borderTop: "15px solid rgba(15,23,42,0.1)"
94
- }), Object.assign(f.style, {
94
+ }), Object.assign(k.style, {
95
95
  position: "absolute",
96
96
  bottom: "2px",
97
97
  left: "0",
@@ -104,9 +104,9 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
104
104
  borderTop: "13px solid white",
105
105
  boxShadow: "0 6px 16px rgba(15,23,42,0.12)",
106
106
  borderRadius: "2px"
107
- }), l.appendChild(b), l.appendChild(f);
108
- const p = document.createElement("div");
109
- Object.assign(p.style, {
107
+ }), m.appendChild(E), m.appendChild(k);
108
+ const f = document.createElement("div");
109
+ Object.assign(f.style, {
110
110
  width: "720px",
111
111
  maxWidth: "calc(100vw - 48px)",
112
112
  height: `${Math.min(720, Math.max(320, window.innerHeight - 140))}px`,
@@ -116,84 +116,175 @@ const y = ({ iframeUrl: h, launcherIcon: u }) => {
116
116
  border: "1px solid rgba(15,23,42,0.12)",
117
117
  boxShadow: "0 18px 45px rgba(15,23,42,0.16)",
118
118
  overflow: "hidden"
119
- }), n.appendChild(p);
120
- const i = document.createElement("iframe");
121
- i.src = h, i.title = "Copilot chat widget", i.allow = "clipboard-read; clipboard-write; microphone; camera; display-capture", i.setAttribute("scrolling", "no"), Object.assign(i.style, {
119
+ }), a.appendChild(f);
120
+ const r = document.createElement("iframe");
121
+ r.src = b, r.title = "Copilot chat widget", r.allow = "clipboard-read; clipboard-write; microphone; camera; display-capture", r.setAttribute("scrolling", "no"), Object.assign(r.style, {
122
122
  width: "100%",
123
123
  height: "100%",
124
124
  border: "none",
125
125
  display: "block",
126
126
  background: "transparent",
127
127
  overflow: "hidden"
128
- }), n.appendChild(p), p.appendChild(i), e.appendChild(l), e.appendChild(p), document.body.appendChild(t), document.body.appendChild(e);
129
- let a = !1;
130
- const m = () => {
131
- a && (a = !1, e.style.opacity = "0", e.style.transform = "scale(0.8) translateY(20px)", l.style.display = "none", setTimeout(() => {
132
- e.style.display = "none";
133
- }, 250), t.style.transform = "scale(1)");
134
- }, x = (s) => {
135
- const { data: o, source: r } = s || {};
136
- if (o?.type) {
137
- if (o.type === "CART_CHECKOUT" && r === i.contentWindow) {
138
- console.log("[CopilotChat] Received checkout payload from widget:", o), g("Checkout message received from chat widget. Check console for payload.");
128
+ }), a.appendChild(f), f.appendChild(r), i.appendChild(m), i.appendChild(f), document.body.appendChild(o), document.body.appendChild(i);
129
+ let d = !1;
130
+ const _ = () => {
131
+ d && (d = !1, i.style.opacity = "0", i.style.transform = "scale(0.8) translateY(20px)", m.style.display = "none", setTimeout(() => {
132
+ i.style.display = "none";
133
+ }, 250), o.style.transform = "scale(1)");
134
+ }, T = (t, e = null) => {
135
+ if (!t) return e;
136
+ try {
137
+ return JSON.parse(t);
138
+ } catch {
139
+ return e;
140
+ }
141
+ }, v = (t) => String(t).toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), s = (t, e) => ({
142
+ value: t,
143
+ currency: e,
144
+ __typename: "Money"
145
+ }), O = (t, e) => ({
146
+ maximum_price: {
147
+ discount: { amount_off: 0, percent_off: 0, __typename: "ProductDiscount" },
148
+ final_price: s(t, e),
149
+ fixed_product_taxes: [],
150
+ regular_price: s(t, e),
151
+ __typename: "ProductPrice"
152
+ },
153
+ minimum_price: {
154
+ discount: { amount_off: 0, percent_off: 0, __typename: "ProductDiscount" },
155
+ final_price: s(t, e),
156
+ fixed_product_taxes: [],
157
+ regular_price: s(t, e),
158
+ __typename: "ProductPrice"
159
+ },
160
+ __typename: "PriceRange"
161
+ }), P = (t, e) => {
162
+ const n = Number(t?.quantity || 0), g = Number(t?.price || t?.unitPrice || 0), c = g * n, p = t?.name || "Chat widget product", I = t?.image || t?.thumbnail || "", h = Number(t?.productId || t?.id || 0), l = String(t?.sku || t?.id || h || "chat-widget");
163
+ return {
164
+ id: String(t?.id || l || h || Date.now()),
165
+ quantity: n,
166
+ product: {
167
+ id: h || Date.now(),
168
+ sku: l,
169
+ name: p,
170
+ url_key: t?.url_key || v(p),
171
+ url_suffix: ".html",
172
+ stock_status: "IN_STOCK",
173
+ brand: null,
174
+ has_gift: null,
175
+ price_range: O(g, e),
176
+ thumbnail: {
177
+ disabled: null,
178
+ label: p,
179
+ position: null,
180
+ url: I,
181
+ __typename: "ProductImage"
182
+ },
183
+ small_image: {
184
+ disabled: null,
185
+ label: p,
186
+ position: null,
187
+ url: I,
188
+ __typename: "ProductImage"
189
+ },
190
+ categories: [],
191
+ special_from_date: null,
192
+ special_price: null,
193
+ special_to_date: null,
194
+ __typename: "SimpleProduct"
195
+ },
196
+ prices: {
197
+ discounts: null,
198
+ price: s(g, e),
199
+ row_total: s(c, e),
200
+ row_total_including_tax: s(c, e),
201
+ total_item_discount: s(0, e),
202
+ __typename: "CartItemPrices"
203
+ },
204
+ __typename: "SimpleCartItem",
205
+ itemTotal: c
206
+ };
207
+ }, A = (t) => {
208
+ const e = T(window.localStorage.getItem("web-cart")), n = Array.isArray(t?.cart) ? t.cart : [], g = t?.currency || "VND", c = n.map((l) => P(l, g)), p = c.reduce((l, S) => l + (S.quantity || 0), 0), I = Number(t?.totalPrice || 0) || c.reduce((l, S) => l + (S.itemTotal || 0), 0), h = {
209
+ totalItems: p,
210
+ total: s(I, g),
211
+ id: e?.id || `chat-widget-${Date.now()}`,
212
+ is_virtual: !1,
213
+ shipping_addresses: Array.isArray(e?.shipping_addresses) ? e.shipping_addresses : [],
214
+ total_quantity: p,
215
+ items: c,
216
+ isCartIdLoading: !1,
217
+ isFetchingCart: !1
218
+ };
219
+ window.localStorage.setItem("web-cart", JSON.stringify(h)), window.dispatchEvent(
220
+ new StorageEvent("storage", {
221
+ key: "web-cart",
222
+ newValue: JSON.stringify(h)
223
+ })
224
+ );
225
+ }, R = (t) => {
226
+ const { data: e, source: n } = t || {};
227
+ if (e?.type) {
228
+ if (e.type === "CART_CHECKOUT" && n === r.contentWindow) {
229
+ console.log("[CopilotChat] Received checkout payload from widget:", e), A(e?.data), y("Cart updated on KidsPlaza demo. Open cart to see changes.");
139
230
  return;
140
231
  }
141
- if (o.type === "WIDGET_READY" && r === i.contentWindow) {
142
- i.contentWindow.postMessage({ type: "INIT_WIDGET" }, "*");
232
+ if (e.type === "WIDGET_READY" && n === r.contentWindow) {
233
+ r.contentWindow.postMessage({ type: "INIT_WIDGET" }, "*");
143
234
  return;
144
235
  }
145
- o.type === "CHAT_CLOSED" && m();
236
+ e.type === "CHAT_CLOSED" && _();
146
237
  }
147
238
  };
148
- window.addEventListener("message", x), t.onclick = (s) => {
149
- s.stopPropagation(), a = !a, a ? (e.style.display = "block", l.style.display = "block", requestAnimationFrame(() => {
150
- e.style.opacity = "1", e.style.transform = "scale(1) translateY(0)";
151
- })) : m();
152
- }, document.addEventListener("click", (s) => {
153
- const o = s.target;
154
- o && a && !e.contains(o) && o !== t && m();
239
+ window.addEventListener("message", R), o.onclick = (t) => {
240
+ t.stopPropagation(), d = !d, d ? (i.style.display = "block", m.style.display = "block", requestAnimationFrame(() => {
241
+ i.style.opacity = "1", i.style.transform = "scale(1) translateY(0)";
242
+ })) : _();
243
+ }, document.addEventListener("click", (t) => {
244
+ const e = t.target;
245
+ e && d && !i.contains(e) && e !== o && _();
155
246
  });
156
- const w = {
157
- close: m,
247
+ const C = {
248
+ close: _,
158
249
  open: () => {
159
- a || t.click();
250
+ d || o.click();
160
251
  }
161
252
  };
162
- return window.CopilotChat = window.CopilotChat || {}, window.CopilotChat.close = w.close, window.CopilotChat.open = w.open, window.CopilotChat.controls = w, w;
253
+ return window.CopilotChat = window.CopilotChat || {}, window.CopilotChat.close = C.close, window.CopilotChat.open = C.open, window.CopilotChat.controls = C, C;
163
254
  };
164
- async function E(h = {}) {
255
+ async function W(b = {}) {
165
256
  if (typeof window > "u" || typeof document > "u")
166
257
  return null;
167
- const u = window.CopilotChatConfig || {}, d = h.token || u.token || typeof window < "u" && window.localStorage?.getItem("copilotChatToken") || null;
168
- if (!d)
258
+ const x = window.CopilotChatConfig || {}, u = b.token || x.token || typeof window < "u" && window.localStorage?.getItem("copilotChatToken") || null;
259
+ if (!u)
169
260
  return console.error(
170
261
  "[CopilotChat] Missing token (provide via loadCopilotChatWidget({ token }) or window.CopilotChatConfig.token)."
171
262
  ), null;
172
- const g = h.baseUrl || u.baseUrl || C, c = `${g.replace(/\/$/, "")}/api/chat-widget/config?token=${encodeURIComponent(
173
- d
263
+ const y = b.baseUrl || x.baseUrl || U, w = `${y.replace(/\/$/, "")}/api/chat-widget/config?token=${encodeURIComponent(
264
+ u
174
265
  )}`;
175
266
  try {
176
- const n = await fetch(c, {
267
+ const a = await fetch(w, {
177
268
  credentials: "omit",
178
269
  mode: "cors"
179
270
  });
180
- if (!n.ok)
181
- throw new Error(`Server responded with ${n.status}`);
182
- const t = await n.json();
183
- if (!t?.iframeUrl || !t?.launcherIcon)
271
+ if (!a.ok)
272
+ throw new Error(`Server responded with ${a.status}`);
273
+ const o = await a.json();
274
+ if (!o?.iframeUrl || !o?.launcherIcon)
184
275
  throw new Error("Received incomplete widget configuration from server.");
185
- return window.CopilotChat = window.CopilotChat || {}, window.CopilotChat.token = d, window.CopilotChat.baseUrl = g, y({
186
- iframeUrl: t.iframeUrl,
187
- launcherIcon: t.launcherIcon
276
+ return window.CopilotChat = window.CopilotChat || {}, window.CopilotChat.token = u, window.CopilotChat.baseUrl = y, N({
277
+ iframeUrl: o.iframeUrl,
278
+ launcherIcon: o.launcherIcon
188
279
  });
189
- } catch (n) {
280
+ } catch (a) {
190
281
  return console.error(
191
282
  "[CopilotChat] Error during widget bootstrap:",
192
- n instanceof Error ? n.message : n
283
+ a instanceof Error ? a.message : a
193
284
  ), null;
194
285
  }
195
286
  }
196
287
  export {
197
- E as default,
198
- E as loadCopilotChatWidget
288
+ W as default,
289
+ W as loadCopilotChatWidget
199
290
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copilot-chat-widget",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Embeddable Copilot chat widget that can be loaded via NPM or a script tag.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/index.js CHANGED
@@ -191,7 +191,7 @@ const buildWidget = ({ iframeUrl, launcherIcon }) => {
191
191
 
192
192
  let isOpen = false;
193
193
 
194
- const closeChat = () => {
194
+ const closeChat = () => {
195
195
  if (!isOpen) return;
196
196
  isOpen = false;
197
197
  container.style.opacity = "0";
@@ -201,17 +201,146 @@ const buildWidget = ({ iframeUrl, launcherIcon }) => {
201
201
  container.style.display = "none";
202
202
  }, 250);
203
203
  btn.style.transform = "scale(1)";
204
- };
205
-
206
- const handleWidgetMessage = (event) => {
207
- const { data, source } = event || {};
208
- if (!data?.type) return;
209
-
210
- if (data.type === "CART_CHECKOUT" && source === iframe.contentWindow) {
211
- console.log("[CopilotChat] Received checkout payload from widget:", data);
212
- showCheckoutToast("Checkout message received from chat widget. Check console for payload.");
213
- return;
214
- }
204
+ };
205
+
206
+ const parseJSON = (value, fallback = null) => {
207
+ if (!value) return fallback;
208
+ try {
209
+ return JSON.parse(value);
210
+ } catch (error) {
211
+ return fallback;
212
+ }
213
+ };
214
+
215
+ const slugify = (value) =>
216
+ String(value || "")
217
+ .toLowerCase()
218
+ .trim()
219
+ .replace(/[^a-z0-9]+/g, "-")
220
+ .replace(/^-+|-+$/g, "");
221
+
222
+ const createMoney = (value, currency) => ({
223
+ value,
224
+ currency,
225
+ __typename: "Money",
226
+ });
227
+
228
+ const createPriceRange = (value, currency) => ({
229
+ maximum_price: {
230
+ discount: { amount_off: 0, percent_off: 0, __typename: "ProductDiscount" },
231
+ final_price: createMoney(value, currency),
232
+ fixed_product_taxes: [],
233
+ regular_price: createMoney(value, currency),
234
+ __typename: "ProductPrice",
235
+ },
236
+ minimum_price: {
237
+ discount: { amount_off: 0, percent_off: 0, __typename: "ProductDiscount" },
238
+ final_price: createMoney(value, currency),
239
+ fixed_product_taxes: [],
240
+ regular_price: createMoney(value, currency),
241
+ __typename: "ProductPrice",
242
+ },
243
+ __typename: "PriceRange",
244
+ });
245
+
246
+ const mapToKidsPlazaItem = (item, currency) => {
247
+ const quantity = Number(item?.quantity || 0);
248
+ const unitPrice = Number(item?.price || item?.unitPrice || 0);
249
+ const rowTotalValue = unitPrice * quantity;
250
+ const name = item?.name || "Chat widget product";
251
+ const imageUrl = item?.image || item?.thumbnail || "";
252
+ const productId = Number(item?.productId || item?.id || 0);
253
+ const productSku = String(item?.sku || item?.id || productId || "chat-widget");
254
+
255
+ return {
256
+ id: String(item?.id || productSku || productId || Date.now()),
257
+ quantity,
258
+ product: {
259
+ id: productId || Date.now(),
260
+ sku: productSku,
261
+ name,
262
+ url_key: item?.url_key || slugify(name),
263
+ url_suffix: ".html",
264
+ stock_status: "IN_STOCK",
265
+ brand: null,
266
+ has_gift: null,
267
+ price_range: createPriceRange(unitPrice, currency),
268
+ thumbnail: {
269
+ disabled: null,
270
+ label: name,
271
+ position: null,
272
+ url: imageUrl,
273
+ __typename: "ProductImage",
274
+ },
275
+ small_image: {
276
+ disabled: null,
277
+ label: name,
278
+ position: null,
279
+ url: imageUrl,
280
+ __typename: "ProductImage",
281
+ },
282
+ categories: [],
283
+ special_from_date: null,
284
+ special_price: null,
285
+ special_to_date: null,
286
+ __typename: "SimpleProduct",
287
+ },
288
+ prices: {
289
+ discounts: null,
290
+ price: createMoney(unitPrice, currency),
291
+ row_total: createMoney(rowTotalValue, currency),
292
+ row_total_including_tax: createMoney(rowTotalValue, currency),
293
+ total_item_discount: createMoney(0, currency),
294
+ __typename: "CartItemPrices",
295
+ },
296
+ __typename: "SimpleCartItem",
297
+ itemTotal: rowTotalValue,
298
+ };
299
+ };
300
+
301
+ const updateKidsPlazaCart = (payload) => {
302
+ const existingCart = parseJSON(window.localStorage.getItem("web-cart"));
303
+ const items = Array.isArray(payload?.cart) ? payload.cart : [];
304
+ const currency = payload?.currency || "VND";
305
+ const mappedItems = items.map((item) => mapToKidsPlazaItem(item, currency));
306
+ const totalItems = mappedItems.reduce((sum, item) => sum + (item.quantity || 0), 0);
307
+ const totalValue =
308
+ Number(payload?.totalPrice || 0) ||
309
+ mappedItems.reduce((sum, item) => sum + (item.itemTotal || 0), 0);
310
+
311
+ const cart = {
312
+ totalItems,
313
+ total: createMoney(totalValue, currency),
314
+ id: existingCart?.id || `chat-widget-${Date.now()}`,
315
+ is_virtual: false,
316
+ shipping_addresses: Array.isArray(existingCart?.shipping_addresses)
317
+ ? existingCart.shipping_addresses
318
+ : [],
319
+ total_quantity: totalItems,
320
+ items: mappedItems,
321
+ isCartIdLoading: false,
322
+ isFetchingCart: false,
323
+ };
324
+
325
+ window.localStorage.setItem("web-cart", JSON.stringify(cart));
326
+ window.dispatchEvent(
327
+ new StorageEvent("storage", {
328
+ key: "web-cart",
329
+ newValue: JSON.stringify(cart),
330
+ })
331
+ );
332
+ };
333
+
334
+ const handleWidgetMessage = (event) => {
335
+ const { data, source } = event || {};
336
+ if (!data?.type) return;
337
+
338
+ if (data.type === "CART_CHECKOUT" && source === iframe.contentWindow) {
339
+ console.log("[CopilotChat] Received checkout payload from widget:", data);
340
+ updateKidsPlazaCart(data?.data);
341
+ showCheckoutToast("Cart updated on KidsPlaza demo. Open cart to see changes.");
342
+ return;
343
+ }
215
344
 
216
345
  if (data.type === "WIDGET_READY" && source === iframe.contentWindow) {
217
346
  iframe.contentWindow.postMessage({ type: "INIT_WIDGET" }, "*");