@phygitallabs/phygital-consent 1.0.21 → 1.0.22

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,5 +1,5 @@
1
1
  'use client'
2
2
  "use strict";var ge=Object.create;var W=Object.defineProperty;var Ce=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var ye=Object.getPrototypeOf,be=Object.prototype.hasOwnProperty;var xe=(e,t)=>{for(var n in t)W(e,n,{get:t[n],enumerable:!0})},ne=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of he(t))!be.call(e,s)&&s!==n&&W(e,s,{get:()=>t[s],enumerable:!(o=Ce(t,s))||o.enumerable});return e};var ve=(e,t,n)=>(n=e!=null?ge(ye(e)):{},ne(t||!e||!e.__esModule?W(n,"default",{value:e,enumerable:!0}):n,e)),Pe=e=>ne(W({},"__esModule",{value:!0}),e);var He={};xe(He,{COOKIE_CONSENT_NAME:()=>F,CookieConsentBanner:()=>Z,EntityStatus:()=>se,MediaType:()=>oe,PhygitalConsentProvider:()=>qe,PolicyPopup:()=>Me,consentQueryKeys:()=>h,consentService:()=>v,getConsentPreferenceCookie:()=>T,getCookie:()=>re,isAdvertisingAllowed:()=>ee,isAnalyticsAllowed:()=>L,policyQueryKeys:()=>J,setConsentPreferenceCookie:()=>V,setCookie:()=>ie,sha256:()=>_,useConsentById:()=>Be,useCreateDeviceCookieConsent:()=>Y,useCreateUserConsent:()=>j,useHealth:()=>Ue,useIsAnalyticsAllowed:()=>Qe,useLatestConsentByUserId:()=>Te,useLatestCookieConsentByDeviceId:()=>Oe,useManyConsents:()=>_e,usePhygitalConsent:()=>G,usePolicies:()=>X});module.exports=Pe(He);var oe=(n=>(n.IMAGE="image",n.VIDEO="video",n))(oe||{}),se=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(se||{});var w=require("react");var E=require("react");var F="phygital_cookie_consent";function re(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function ie(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:a="Lax",secure:d=!1}=n,l=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${a}`;d&&(l+="; Secure"),document.cookie=l}function T(){let e=re(F);if(!e)return null;try{let t=JSON.parse(e);if(t&&typeof t=="object"&&"deviceId"in t&&"selected_preferences"in t&&typeof t.deviceId=="string"&&Array.isArray(t.selected_preferences))return t}catch{}return null}function V(e,t,n){ie(F,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}var x=require("@tanstack/react-query");var U="",v=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${U}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${U}/consent/${t}`);return n},getPolicies:async t=>{let{data:n}=await e.get(`${U}/policy`,{params:t??void 0});return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${U}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${U}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${U}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${U}/cookies/device-id`,t);return n}});function ke(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("").toLowerCase()}async function Re(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return ke(n)}function we(e){let t=Se(e),n=Ie(),o=Ae(),s=new Uint32Array(64),a=t.length>>6;for(let l=0;l<a;l++){let u=l<<6;for(let p=0;p<16;p++){let b=u+(p<<2);s[p]=t[b]<<24|t[b+1]<<16|t[b+2]<<8|t[b+3]}for(let p=16;p<64;p++){let b=k(s[p-15],7)^k(s[p-15],18)^s[p-15]>>>3,N=k(s[p-2],17)^k(s[p-2],19)^s[p-2]>>>10;s[p]=s[p-16]+b+s[p-7]+N>>>0}let c=o[0],S=o[1],y=o[2],D=o[3],C=o[4],P=o[5],I=o[6],m=o[7];for(let p=0;p<64;p++){let b=k(C,6)^k(C,11)^k(C,25),N=C&P^~C&I,q=m+b+N+n[p]+s[p]>>>0,H=k(c,2)^k(c,13)^k(c,22),K=c&S^c&y^S&y,i=H+K>>>0;m=I,I=P,P=C,C=D+q>>>0,D=y,y=S,S=c,c=q+i>>>0}o[0]=o[0]+c>>>0,o[1]=o[1]+S>>>0,o[2]=o[2]+y>>>0,o[3]=o[3]+D>>>0,o[4]=o[4]+C>>>0,o[5]=o[5]+P>>>0,o[6]=o[6]+I>>>0,o[7]=o[7]+m>>>0}let d="";for(let l=0;l<8;l++){let u=o[l];d+=(u>>>28).toString(16)+(u>>>24&15).toString(16)+(u>>>20&15).toString(16)+(u>>>16&15).toString(16)+(u>>>12&15).toString(16)+(u>>>8&15).toString(16)+(u>>>4&15).toString(16)+(u&15).toString(16)}return d.toLowerCase()}function k(e,t){return e>>>t|e<<32-t}function Se(e){let t=e.length,n=[];for(let u=0;u<t;u++){let c=e.charCodeAt(u);c<128?n.push(c):c<2048?n.push(192|c>>6,128|c&63):c<55296||c>=57344?n.push(224|c>>12,128|c>>6&63,128|c&63):(c=65536+((c&1023)<<10|e.charCodeAt(++u)&1023),n.push(240|c>>18,128|c>>12&63,128|c>>6&63,128|c&63))}let o=n.length,s=(64-(o+9)%64)%64,a=o+9+s,d=new Uint8Array(a);d.set(n),d[o]=128;let l=new DataView(d.buffer,d.byteOffset,d.byteLength);return l.setUint32(a-8,0,!1),l.setUint32(a-4,o*8>>>0,!1),d}function Ie(){let e=[];return"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2".split(" ").forEach(n=>e.push(parseInt(n,16))),e}function Ae(){return[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]}function Ee(){return!(typeof crypto>"u"||!crypto.subtle)}async function _(e){return typeof e!="string"||e.length===0?e:Ee()?Re(e):Promise.resolve(we(e))}var M=require("@tanstack/react-query"),ce=ve(require("axios")),B=require("react"),$=require("react/jsx-runtime"),ae=(0,B.createContext)(void 0),De=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=ce.default.create({baseURL:e,...t});return s.interceptors.request.use(n.onFulfilled,n.onRejected),s.interceptors.response.use(o.onFulfilled,o.onRejected),{consentApi:s,updateHeaders:d=>{Object.entries(d).forEach(([l,u])=>{s.defaults.headers.common[l]=u})}}},de=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:a={},responseInterceptors:d={}})=>{let l=(0,B.useMemo)(()=>o||new M.QueryClient({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),u=(0,B.useMemo)(()=>{let c=De({baseURL:t,axiosConfig:n,requestInterceptors:a,responseInterceptors:d});return{consentApi:c.consentApi,updateHeaders:c.updateHeaders,queryClient:l}},[t,l,n,a,d]);return(0,$.jsx)(ae.Provider,{value:u,children:(0,$.jsx)(M.QueryClientProvider,{client:l,children:e})})},R=()=>{let e=(0,B.useContext)(ae);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};var h={all:["consent"],health:()=>[...h.all,"health"],list:e=>[...h.all,"list",e],detail:e=>[...h.all,"detail",e],latestByUserId:e=>[...h.all,"user-id",e],latestCookieByDeviceId:e=>[...h.all,"cookies","device-id",e]},Ue=e=>{let{consentApi:t}=R(),n=v(t);return(0,x.useQuery)({queryKey:h.health(),queryFn:()=>n.getHealth(),...e})},_e=(e,t)=>{let{consentApi:n}=R(),o=v(n);return(0,x.useQuery)({queryKey:h.list(e),queryFn:()=>o.getManyConsents(e),...t})},Be=(e,t)=>{let{consentApi:n}=R(),o=v(n);return(0,x.useQuery)({queryKey:h.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},Te=(e,t)=>{let{consentApi:n}=R(),o=v(n);return(0,x.useQuery)({queryKey:h.latestByUserId(e.user_id),queryFn:async()=>{let s=await _(e.user_id);return o.getLatestConsentByUserId({user_id:s})},enabled:!!e.user_id,...t})},Oe=(e,t)=>{let{consentApi:n}=R(),o=v(n);return(0,x.useQuery)({queryKey:h.latestCookieByDeviceId(e.device_id),queryFn:async()=>{let s=await _(e.device_id);return o.getLatestCookieConsentByDeviceId({device_id:s})},enabled:!!e.device_id,...t})},j=e=>{let{consentApi:t}=R(),n=(0,x.useQueryClient)(),o=v(t);return(0,x.useMutation)({mutationFn:async s=>{let a={...s};return s.user_id!=null&&s.user_id!==""&&(a.user_id=await _(s.user_id)),s.device_id!=null&&s.device_id!==""&&(a.device_id=await _(s.device_id)),o.createUserConsent(a)},onSuccess:(s,a)=>{a.user_id&&n.invalidateQueries({queryKey:h.latestByUserId(a.user_id)}),n.invalidateQueries({queryKey:h.list()})},...e})},Y=e=>{let{consentApi:t}=R(),n=(0,x.useQueryClient)(),o=v(t);return(0,x.useMutation)({mutationFn:async s=>{let a={...s,device_id:await _(s.device_id)};return o.createDeviceCookieConsent(a)},onSuccess:(s,a)=>{n.invalidateQueries({queryKey:h.latestCookieByDeviceId(a.device_id)}),n.invalidateQueries({queryKey:h.list()})},...e})};var le=require("@tanstack/react-query");var J={all:["policy"],list:e=>[...J.all,"list",e]},X=(e,t)=>{let{consentApi:n}=R(),o=v(n);return(0,le.useQuery)({queryKey:J.list(e),queryFn:()=>o.getPolicies(e),...t})};var r=require("react/jsx-runtime");function Z({onSubmitted:e,onDismiss:t,className:n=""}){let[o,s]=(0,E.useState)(!1),[a,d]=(0,E.useState)(!1),[l,u]=(0,E.useState)(null),[c,S]=(0,E.useState)({}),{data:y}=X({type:"COOKIE_PREFERENCE",status:"ACTIVE"}),C=(0,E.useMemo)(()=>y?.data?y?.data?.find(i=>i.type==="COOKIE_PREFERENCE"&&i.status==="ACTIVE")??y?.data?.[0]:null,[y])?.preferences??[],P=(0,E.useMemo)(()=>({...Object.fromEntries(C.map(g=>[g.id??"",!0]).filter(([g])=>!!g)),...c}),[C,c]),{deviceId:I}=G(),m=Y({onSuccess:(i,g)=>{V(I,g.selected_preferences??[]),e?.(),t?.(),s(!0),d(!1)}}),p=()=>C.map(i=>i.id).filter(i=>!!i).filter(i=>P[i]),b=(i,g)=>({device_id:I,status:i,selected_preferences:g,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),N=i=>{C.find(A=>A.id===i)?.is_mandatory||S(A=>({...A,[i]:!P[i]}))},q=()=>{m.mutate(b("REJECTED",[]))},H=()=>{let i=C.map(g=>g.id).filter(g=>!!g);m.mutate(b("ACCEPTED",i.length>0?i:[]))},K=()=>{let i=p();m.mutate(b("ACCEPTED",i.length>0?i:["essential"]))};return o?null:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{role:"dialog","aria-label":"Th\xF4ng b\xE1o v\u1EC1 cookie",className:`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${n}`,style:{position:"fixed",bottom:0,left:0,right:0,zIndex:50,display:"flex",flexDirection:"column",gap:"1rem",borderRadius:"0.75rem 0.75rem 0 0",borderWidth:"1px 1px 0 1px",borderColor:"#e5e7eb",backgroundColor:"#fff",padding:"1rem",boxShadow:"0 -4px 6px -1px rgb(0 0 0 / 0.1)"},children:[(0,r.jsx)("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:"Th\xF4ng b\xE1o v\u1EC1 cookie"}),(0,r.jsxs)("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",display:"flex",flexDirection:"column",gap:"0.5rem"},children:[(0,r.jsx)("p",{children:"TapQuest s\u1EED d\u1EE5ng tr\xECnh theo d\xF5i (nh\u01B0 Cookie ho\u1EB7c SDK) cho:"}),(0,r.jsx)("ul",{style:{margin:0,paddingLeft:"1.25rem"},children:C.map(i=>(0,r.jsx)("li",{children:i.name??i.id??""},i.id??i.name))}),(0,r.jsx)("p",{children:"Tr\u01B0\u1EDBc khi b\u1EA1n \u0111\u1ED3ng \xFD, ch\u1EC9 nh\u1EEFng tr\xECnh theo d\xF5i ho\xE0n to\xE0n c\u1EA7n thi\u1EBFt m\u1EDBi \u0111\u01B0\u1EE3c tri\u1EC3n khai."}),(0,r.jsx)("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn Ch\u1EA5p nh\u1EADn t\u1EA5t c\u1EA3, b\u1EA1n \u0111\u1ED3ng \xFD s\u1EED d\u1EE5ng t\u1EA5t c\u1EA3 tr\xECnh theo d\xF5i c\u1EE7a ch\xFAng t\xF4i."}),(0,r.jsx)("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn T\u1EEB ch\u1ED1i t\u1EA5t c\u1EA3, b\u1EA1n t\u1EEB ch\u1ED1i nh\u1EEFng tr\xECnh theo d\xF5i y\xEAu c\u1EA7u s\u1EF1 \u0111\u1ED3ng \xFD c\u1EE7a b\u1EA1n v\xE0 s\u1EBD kh\xF4ng c\xF3 quy\u1EC1n truy c\u1EADp v\xE0o c\xE1c \u0111\u1EC1 ngh\u1ECB ho\u1EB7c n\u1ED9i dung \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a."}),(0,r.jsx)("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn T\xF9y ch\u1ECDn, b\u1EA1n c\xF3 th\u1EC3 ch\u1ECDn xem b\u1EA1n c\xF3 \u0111\u1ED3ng \xFD s\u1EED d\u1EE5ng tr\xECnh theo d\xF5i tr\xEAn \u1EE9ng d\u1EE5ng c\u1EE7a ch\xFAng t\xF4i hay kh\xF4ng v\xE0 \u1EDF m\u1EE9c \u0111\u1ED9 n\xE0o."}),(0,r.jsxs)("p",{children:["B\u1EA1n c\xF3 th\u1EC3 r\xFAt l\u1EA1i s\u1EF1 \u0111\u1ED3ng \xFD c\u1EE7a m\xECnh b\u1EA5t k\u1EF3 l\xFAc n\xE0o b\u1EB1ng c\xE1ch nh\u1EA5p v\xE0o li\xEAn k\u1EBFt trong"," ","Ch\xEDnh s\xE1ch cookie"," ","c\u1EE7a ch\xFAng t\xF4i."]})]}),(0,r.jsxs)("div",{className:"flex justify-end gap-2 flex-wrap",children:[(0,r.jsx)("button",{type:"button",onClick:()=>d(!0),className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\xF9y ch\u1ECDn"}),(0,r.jsx)("button",{type:"button",onClick:q,disabled:m.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i t\u1EA5t c\u1EA3"}),(0,r.jsx)("button",{type:"button",onClick:H,disabled:m.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"Ch\u1EA5p nh\u1EADn t\u1EA5t c\u1EA3"})]}),m.isError&&(0,r.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]}),a&&(0,r.jsx)("div",{role:"dialog","aria-modal":"true","aria-label":"Trung t\xE2m tu\u1EF3 ch\u1ECDn b\u1EA3o m\u1EADt",style:{position:"fixed",inset:0,zIndex:60,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:i=>i.target===i.currentTarget&&d(!1),children:(0,r.jsxs)("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"28rem",width:"100%",maxHeight:"90vh",overflowY:"auto",padding:"1.25rem",position:"relative"},onClick:i=>i.stopPropagation(),children:[(0,r.jsx)("button",{type:"button",onClick:()=>d(!1),"aria-label":"\u0110\xF3ng",style:{position:"absolute",top:"1rem",right:"1rem",width:"2rem",height:"2rem",borderRadius:"0.25rem",border:"none",background:"transparent",cursor:"pointer",fontSize:"1.25rem",lineHeight:1,color:"#6b7280"},children:"\xD7"}),(0,r.jsx)("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827",marginBottom:"0.75rem",paddingRight:"2rem"},children:"Trung t\xE2m tu\u1EF3 ch\u1ECDn b\u1EA3o m\u1EADt"}),(0,r.jsxs)("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",marginBottom:"1rem"},children:[(0,r.jsx)("p",{style:{marginBottom:"0.5rem"},children:"Khi b\u1EA1n truy c\u1EADp b\u1EA5t k\u1EF3 trang web n\xE0o, trang web \u0111\xF3 c\xF3 th\u1EC3 l\u01B0u tr\u1EEF ho\u1EB7c truy xu\u1EA5t th\xF4ng tin tr\xEAn tr\xECnh duy\u1EC7t c\u1EE7a b\u1EA1n, ch\u1EE7 y\u1EBFu d\u01B0\u1EDBi d\u1EA1ng cookie. Th\xF4ng tin n\xE0y c\xF3 th\u1EC3 li\xEAn quan \u0111\u1EBFn b\u1EA1n, t\xF9y ch\u1ECDn c\u1EE7a b\u1EA1n ho\u1EB7c thi\u1EBFt b\u1ECB c\u1EE7a b\u1EA1n, v\xE0 ch\u1EE7 y\u1EBFu \u0111\u01B0\u1EE3c s\u1EED d\u1EE5ng \u0111\u1EC3 trang web ho\u1EA1t \u0111\u1ED9ng nh\u01B0 mong \u0111\u1EE3i."}),(0,r.jsx)("p",{style:{marginBottom:"0.5rem"},children:"Th\xF4ng tin n\xE0y th\u01B0\u1EDDng kh\xF4ng tr\u1EF1c ti\u1EBFp x\xE1c \u0111\u1ECBnh b\u1EA1n nh\u01B0ng c\xF3 th\u1EC3 mang l\u1EA1i tr\u1EA3i nghi\u1EC7m web \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a h\u01A1n. Ch\xFAng t\xF4i t\xF4n tr\u1ECDng quy\u1EC1n ri\xEAng t\u01B0 c\u1EE7a b\u1EA1n; b\u1EA1n c\xF3 th\u1EC3 ch\u1ECDn kh\xF4ng cho ph\xE9p m\u1ED9t s\u1ED1 lo\u1EA1i cookie. Nh\u1EA5p v\xE0o ti\xEAu \u0111\u1EC1 t\u1EEBng danh m\u1EE5c \u0111\u1EC3 t\xECm hi\u1EC3u th\xEAm v\xE0 thay \u0111\u1ED5i c\xE0i \u0111\u1EB7t m\u1EB7c \u0111\u1ECBnh. Vi\u1EC7c ch\u1EB7n m\u1ED9t s\u1ED1 cookie c\xF3 th\u1EC3 \u1EA3nh h\u01B0\u1EDFng \u0111\u1EBFn tr\u1EA3i nghi\u1EC7m c\u1EE7a b\u1EA1n v\u1EDBi trang web v\xE0 c\xE1c d\u1ECBch v\u1EE5 \u0111\u01B0\u1EE3c cung c\u1EA5p."}),(0,r.jsx)("p",{style:{marginBottom:"0.5rem"},children:'N\u1EBFu ch\u1ECDn "Kh\xF4ng theo d\xF5i" khi l\u1EA7n \u0111\u1EA7u s\u1EED d\u1EE5ng TapQuest, ch\u1EC9 cookie ho\xE0n to\xE0n c\u1EA7n thi\u1EBFt m\u1EDBi \u0111\u01B0\u1EE3c s\u1EED d\u1EE5ng.'}),(0,r.jsx)("span",{style:{color:"#2563eb",textDecoration:"underline"},children:"Th\xF4ng tin kh\xE1c"})]}),(0,r.jsx)("h3",{className:"consent:text-sm consent:font-semibold consent:text-gray-900",style:{fontSize:"0.875rem",fontWeight:600,color:"#111827",marginBottom:"0.75rem"},children:"Qu\u1EA3n l\xFD tu\u1EF3 ch\u1ECDn \u0111\u1ED3ng \xFD"}),(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"0.25rem",marginBottom:"1.25rem"},children:C.map(i=>{let g=i.id??i.name??"",A=P[g],Q=l===g,z=i.is_mandatory===!0;return(0,r.jsxs)("div",{style:{border:"1px solid #e5e7eb",borderRadius:"0.5rem",overflow:"hidden"},children:[(0,r.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.75rem",cursor:"pointer",backgroundColor:Q?"#f9fafb":"transparent"},onClick:()=>u(Q?null:g),children:[(0,r.jsx)("span",{style:{flexShrink:0,width:"1.25rem",height:"1.25rem",display:"flex",alignItems:"center",justifyContent:"center",fontSize:"1rem",color:"#6b7280",transform:Q?"rotate(45deg)":"none",transition:"transform 0.2s"},children:"+"}),(0,r.jsx)("span",{className:"consent:font-medium consent:text-gray-900",style:{flex:1,fontSize:"0.875rem",fontWeight:500,color:"#111827"},children:i.name??g}),(0,r.jsx)("button",{type:"button",role:"switch","aria-checked":A,"aria-label":`${i.name??g} ${A?"b\u1EADt":"t\u1EAFt"}`,disabled:z,onClick:me=>{me.stopPropagation(),N(g)},style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:z?"default":"pointer",backgroundColor:z?"#e5e7eb":A?"#111827":"#d1d5db",opacity:z?.7:1,transition:"background-color 0.2s"},children:(0,r.jsx)("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:A?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]}),Q&&(0,r.jsx)("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",padding:"0.75rem 0.75rem 0.75rem 2.5rem",borderTop:"1px solid #e5e7eb"},children:i.description??""})]},g)})}),(0,r.jsx)("button",{type:"button",onClick:K,disabled:m.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer",width:"100%"},children:"X\xE1c nh\u1EADn l\u1EF1a ch\u1ECDn c\u1EE7a t\xF4i"})]})})]})}var Ne="analytics",Ge="advertising";function L(){let e=T();return e?.selected_preferences?e.selected_preferences.includes(Ne):!1}function ee(){let e=T();return e?.selected_preferences?e.selected_preferences.includes(Ge):!1}var O=require("react/jsx-runtime"),fe=(0,w.createContext)(void 0);function Le({children:e,deviceId:t,showBannerWhenNoPreference:n=!0}){let[o,s]=(0,w.useState)(!1),a=(0,w.useCallback)(()=>{let l=T();s(!!l)},[]);(0,w.useEffect)(()=>{a()},[a]);let d={deviceId:t,hasConsentPreference:o,isAnalyticsAllowed:L(),isAdvertisingAllowed:ee(),refreshConsentPreference:a};return(0,O.jsxs)(fe.Provider,{value:d,children:[e,n&&!o&&(0,O.jsx)(Z,{onSubmitted:a})]})}function qe(e){let{children:t,deviceId:n,showBannerWhenNoPreference:o=!0,...s}=e;return(0,O.jsx)(de,{...s,children:(0,O.jsx)(Le,{deviceId:n,showBannerWhenNoPreference:o,children:t})})}function G(){let e=(0,w.useContext)(fe);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}function Qe(){return G(),L()}var te=require("react");var f=require("react/jsx-runtime"),ze=`
3
3
  Tanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.
4
- `.trim(),We="V\xEC b\u1EA1n kh\xF4ng \u0111\u1ED3ng \xFD v\u1EDBi th\u1ECFa thu\u1EADn n\xE0y. Ch\xFAng t\xF4i s\u1EBD x\xF3a t\xE0i kho\u1EA3n trong v\xF2ng x ng\xE0y. Nh\u1EA5n n\xFAt '\u0110\u1ED3ng \xFD' \u0111\u1EC3 ti\u1EBFp t\u1EE5c";function Me({open:e,onClose:t,onReject:n,userId:o,onAgreeSuccess:s}){let[a,d]=(0,te.useState)(!1),[l,u]=(0,te.useState)(!1),c=j({onSuccess:(m,p)=>{p.status==="ACCEPTED"&&s?.(),S()}}),S=()=>{d(!1),u(!1),t()},y=()=>{u(!0)},D=()=>{u(!1)},C=m=>({user_id:o,status:m,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),P=()=>{c.mutate(C("REJECTED")),n?.()},I=()=>{a&&c.mutate(C("ACCEPTED"))};return e?(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",padding:"1rem"},children:(0,f.jsxs)("div",{className:"consent:overflow-hidden consent:flex consent:flex-col",style:{width:"100%",height:"100%",overflow:"hidden",display:"flex",flexDirection:"column"},onClick:m=>m.stopPropagation(),children:[(0,f.jsx)("h2",{style:{fontSize:"18px",fontWeight:600,color:"#000",padding:"20px 2.5rem 12px 20px"},children:"Ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt"}),(0,f.jsx)("div",{style:{flex:1,overflowY:"auto",padding:"1rem 1.25rem",fontSize:"0.875rem",color:"#111827",lineHeight:1.6},children:(0,f.jsx)("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:ze})}),(0,f.jsxs)("div",{style:{padding:"1rem 1.25rem 1.25rem 1.25rem",display:"flex",flexDirection:"column",gap:"1rem"},children:[(0,f.jsxs)("label",{style:{display:"flex",alignItems:"center",gap:"0.5rem",fontSize:"0.875rem",color:"#111827",cursor:"pointer"},children:[(0,f.jsx)("input",{type:"checkbox",checked:a,onChange:m=>d(m.target.checked),style:{width:"1rem",height:"1rem",accentColor:"#111827"},"aria-label":"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"}),(0,f.jsx)("span",{children:"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"})]}),(0,f.jsxs)("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[(0,f.jsx)("button",{type:"button",onClick:y,disabled:c.isPending,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i"}),(0,f.jsx)("button",{type:"button",onClick:I,disabled:!a||c.isPending,className:"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:a?"#111827":"#e5e7eb",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:a?"#fff":"#9ca3af",border:"none",cursor:a?"pointer":"not-allowed"},children:"Ti\u1EBFp t\u1EE5c"})]}),c.isError&&(0,f.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:0},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})]})}),l&&(0,f.jsx)("div",{role:"alertdialog","aria-modal":"true","aria-label":"Th\xF4ng b\xE1o t\u1EEB ch\u1ED1i",style:{position:"fixed",inset:0,zIndex:70,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:m=>m.target===m.currentTarget&&D(),children:(0,f.jsxs)("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"22rem",width:"100%",padding:"1.25rem"},onClick:m=>m.stopPropagation(),children:[(0,f.jsx)("p",{className:"consent:text-sm consent:text-gray-700",style:{fontSize:"0.875rem",color:"#374151",margin:"0 0 1rem 0",lineHeight:1.5},children:We}),(0,f.jsxs)("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[(0,f.jsx)("button",{type:"button",onClick:D,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"Tr\u1EDF l\u1EA1i"}),(0,f.jsx)("button",{type:"button",onClick:P,disabled:c.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:"#a71f24",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"\u0110\u1ED3ng \xFD"})]}),c.isError&&(0,f.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:"0.5rem 0 0 0"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})})]}):null}0&&(module.exports={COOKIE_CONSENT_NAME,CookieConsentBanner,EntityStatus,MediaType,PhygitalConsentProvider,PolicyPopup,consentQueryKeys,consentService,getConsentPreferenceCookie,getCookie,isAdvertisingAllowed,isAnalyticsAllowed,policyQueryKeys,setConsentPreferenceCookie,setCookie,sha256,useConsentById,useCreateDeviceCookieConsent,useCreateUserConsent,useHealth,useIsAnalyticsAllowed,useLatestConsentByUserId,useLatestCookieConsentByDeviceId,useManyConsents,usePhygitalConsent,usePolicies});
4
+ `.trim(),We="V\xEC b\u1EA1n kh\xF4ng \u0111\u1ED3ng \xFD v\u1EDBi th\u1ECFa thu\u1EADn n\xE0y. Ch\xFAng t\xF4i s\u1EBD x\xF3a t\xE0i kho\u1EA3n trong v\xF2ng x ng\xE0y. Nh\u1EA5n n\xFAt '\u0110\u1ED3ng \xFD' \u0111\u1EC3 ti\u1EBFp t\u1EE5c";function Me({open:e,onClose:t,onReject:n,userId:o,onAgreeSuccess:s}){let[a,d]=(0,te.useState)(!1),[l,u]=(0,te.useState)(!1),c=j({onSuccess:(m,p)=>{p.status==="ACCEPTED"&&s?.(),S()}}),S=()=>{d(!1),u(!1),t()},y=()=>{u(!0)},D=()=>{u(!1)},C=m=>({user_id:o,status:m,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),P=()=>{c.mutate(C("REJECTED")),n?.()},I=()=>{a&&c.mutate(C("ACCEPTED"))};return e?(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center"},children:(0,f.jsxs)("div",{className:"consent:overflow-hidden consent:flex consent:flex-col",style:{width:"100%",height:"100%",overflow:"hidden",display:"flex",flexDirection:"column"},onClick:m=>m.stopPropagation(),children:[(0,f.jsx)("h2",{style:{fontSize:"20px",fontWeight:600,color:"#000",padding:"20px 2.5rem 12px 20px"},children:"Ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt"}),(0,f.jsx)("div",{style:{flex:1,overflowY:"auto",padding:"1rem 1.25rem",fontSize:"0.875rem",color:"#111827",lineHeight:1.6},children:(0,f.jsx)("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:ze})}),(0,f.jsxs)("div",{style:{padding:"1rem 1.25rem 1.25rem 1.25rem",display:"flex",flexDirection:"column",gap:"1rem"},children:[(0,f.jsxs)("label",{style:{display:"flex",alignItems:"center",gap:"0.5rem",fontSize:"0.875rem",color:"#111827",cursor:"pointer"},children:[(0,f.jsx)("input",{type:"checkbox",checked:a,onChange:m=>d(m.target.checked),style:{width:"1rem",height:"1rem",accentColor:"#111827"},"aria-label":"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"}),(0,f.jsx)("span",{children:"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"})]}),(0,f.jsxs)("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[(0,f.jsx)("button",{type:"button",onClick:y,disabled:c.isPending,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i"}),(0,f.jsx)("button",{type:"button",onClick:I,disabled:!a||c.isPending,className:"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:a?"#111827":"#e5e7eb",padding:"0.5rem 1rem",fontSize:"1rem",fontWeight:500,color:a?"#fff":"#9ca3af",border:"none",cursor:a?"pointer":"not-allowed"},children:"Ti\u1EBFp t\u1EE5c"})]}),c.isError&&(0,f.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"1rem",color:"#dc2626",margin:0},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})]})}),l&&(0,f.jsx)("div",{role:"alertdialog","aria-modal":"true","aria-label":"Th\xF4ng b\xE1o t\u1EEB ch\u1ED1i",style:{position:"fixed",inset:0,zIndex:70,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:m=>m.target===m.currentTarget&&D(),children:(0,f.jsxs)("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"22rem",width:"100%",padding:"1.25rem"},onClick:m=>m.stopPropagation(),children:[(0,f.jsx)("p",{className:"consent:text-sm consent:text-gray-700",style:{fontSize:"1rem",color:"#374151",margin:"0 0 1rem 0",lineHeight:1.5},children:We}),(0,f.jsxs)("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[(0,f.jsx)("button",{type:"button",onClick:D,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"Tr\u1EDF l\u1EA1i"}),(0,f.jsx)("button",{type:"button",onClick:P,disabled:c.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:"#a71f24",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"\u0110\u1ED3ng \xFD"})]}),c.isError&&(0,f.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:"0.5rem 0 0 0"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})})]}):null}0&&(module.exports={COOKIE_CONSENT_NAME,CookieConsentBanner,EntityStatus,MediaType,PhygitalConsentProvider,PolicyPopup,consentQueryKeys,consentService,getConsentPreferenceCookie,getCookie,isAdvertisingAllowed,isAnalyticsAllowed,policyQueryKeys,setConsentPreferenceCookie,setCookie,sha256,useConsentById,useCreateDeviceCookieConsent,useCreateUserConsent,useHealth,useIsAnalyticsAllowed,useLatestConsentByUserId,useLatestCookieConsentByDeviceId,useManyConsents,usePhygitalConsent,usePolicies});
5
5
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/components/CookieConsentBanner.tsx","../src/helpers/cookie.ts","../src/hooks/useConsent.ts","../src/api/consent.ts","../src/helpers/sha256.ts","../src/provider/ConsentServiceProvider.tsx","../src/hooks/usePolicy.ts","../src/helpers/analytics-consent.ts","../src/hooks/useAnalyticsConsent.ts","../src/components/PolicyPopup.tsx"],"sourcesContent":["export * from \"./src\";\n","export enum MediaType {\n IMAGE = \"image\",\n VIDEO = \"video\",\n}\n\nexport type DateTimeNumber = number;\n\nexport interface Media {\n url: string;\n type: MediaType | string;\n thumbnail_url?: string;\n}\n\nexport enum EntityStatus {\n ACTIVE = \"Active\",\n INACTIVE = \"Inactive\",\n}\n\nexport interface CommonModel {\n id: string;\n status: EntityStatus;\n created_at: DateTimeNumber;\n updated_at: DateTimeNumber;\n created_by?: string;\n updated_by?: string;\n}\n","\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useState } from \"react\";\nimport { CookieConsentBanner } from \"../components/CookieConsentBanner\";\nimport { isAdvertisingAllowed, isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n deviceId: string;\n hasConsentPreference: boolean;\n isAnalyticsAllowed: boolean;\n isAdvertisingAllowed: boolean;\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n deviceId: string;\n children: React.ReactNode;\n showBannerWhenNoPreference?: boolean;\n}\n\n/** Inner provider: runs inside ConsentServiceProvider so useLatestConsentByUserId has context. */\nfunction PhygitalConsentProviderInner({\n children,\n deviceId,\n showBannerWhenNoPreference = true,\n}: Pick<PhygitalConsentProviderProps, \"children\" | \"deviceId\" | \"showBannerWhenNoPreference\">) {\n const [hasConsentPreference, setHasConsentPreference] = useState(false);\n\n const refreshConsentPreference = useCallback(() => {\n const stored = getConsentPreferenceCookie();\n setHasConsentPreference(!!stored);\n }, []);\n\n useEffect(() => {\n refreshConsentPreference();\n }, [refreshConsentPreference]);\n\n const value: PhygitalConsentContextValue = {\n deviceId,\n hasConsentPreference,\n isAnalyticsAllowed: isAnalyticsAllowed(),\n isAdvertisingAllowed: isAdvertisingAllowed(),\n refreshConsentPreference,\n };\n\n return (\n <PhygitalConsentContext.Provider value={value}>\n {children}\n {showBannerWhenNoPreference && !hasConsentPreference && (\n <CookieConsentBanner onSubmitted={refreshConsentPreference} />\n )}\n </PhygitalConsentContext.Provider>\n );\n}\n\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, deviceId, showBannerWhenNoPreference = true, ...consentServiceProps } = props;\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentProviderInner\n deviceId={deviceId}\n showBannerWhenNoPreference={showBannerWhenNoPreference}\n >\n {children}\n </PhygitalConsentProviderInner>\n </ConsentServiceProvider>\n );\n}\n\nexport function usePhygitalConsent() {\n const context = useContext(PhygitalConsentContext);\n if (!context) {\n throw new Error(\"usePhygitalConsent must be used within a PhygitalConsentProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { COOKIE_POLICY_URL, OTHER_INFO_URL } from \"../env/constant\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\nimport { usePolicies } from \"../hooks/usePolicy\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\nexport interface CookieConsentBannerProps {\n /** Called after consent is submitted successfully (Reject or Accept). */\n onSubmitted?: () => void;\n /** Called when the banner is dismissed (e.g. hide from UI). */\n onDismiss?: () => void;\n /** Optional class name for the root container. */\n className?: string;\n}\n\nexport function CookieConsentBanner({\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [showCustomModal, setShowCustomModal] = useState(false);\n const [expandedKey, setExpandedKey] = useState<string | null>(null);\n const [preferences, setPreferences] = useState<Record<string, boolean>>({});\n\n const { data: policyResponse } = usePolicies({ type: \"COOKIE_PREFERENCE\", status: \"ACTIVE\" });\n\n const policy = useMemo(\n () => {\n if (!policyResponse?.data) return null;\n\n return policyResponse?.data?.find((p) => p.type === \"COOKIE_PREFERENCE\" && p.status === \"ACTIVE\") ?? policyResponse?.data?.[0];\n },\n [policyResponse]\n );\n const preferenceList = policy?.preferences ?? [];\n\n const effectivePreferences = useMemo(() => {\n const defaultAllTrue = Object.fromEntries(\n preferenceList.map((p) => [p.id ?? \"\", true]).filter(([id]) => !!id)\n );\n return { ...defaultAllTrue, ...preferences };\n }, [preferenceList, preferences]);\n\n const { deviceId } = usePhygitalConsent();\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n setShowCustomModal(false);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n preferenceList\n .map((p) => p.id)\n .filter((id): id is string => !!id)\n .filter((id) => effectivePreferences[id]);\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\", selected_preferences: string[]) => ({\n device_id: deviceId,\n status,\n selected_preferences,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const togglePreference = (id: string) => {\n const pref = preferenceList.find((p) => p.id === id);\n if (pref?.is_mandatory) return;\n setPreferences((prev) => ({ ...prev, [id]: !effectivePreferences[id] }));\n };\n\n const handleReject = () => {\n createConsent.mutate(getConsentPayload(\"REJECTED\", []));\n };\n\n const handleAcceptAll = () => {\n const ids = preferenceList.map((p) => p.id).filter((id): id is string => !!id);\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", ids.length > 0 ? ids : []));\n };\n\n const handleSaveCustom = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", selected.length > 0 ? selected : [\"essential\"]));\n };\n\n if (dismissed) return null;\n\n return (\n <>\n {/* Screen 1: Bottom banner */}\n <div\n role=\"dialog\"\n aria-label=\"Thông báo về cookie\"\n className={`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${className}`}\n style={{\n position: \"fixed\",\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 50,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n borderRadius: \"0.75rem 0.75rem 0 0\",\n borderWidth: \"1px 1px 0 1px\",\n borderColor: \"#e5e7eb\",\n backgroundColor: \"#fff\",\n padding: \"1rem\",\n boxShadow: \"0 -4px 6px -1px rgb(0 0 0 / 0.1)\",\n }}\n >\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\" }}\n >\n Thông báo về cookie\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\" }}\n >\n <p>\n TapQuest sử dụng trình theo dõi (như Cookie hoặc SDK) cho:\n </p>\n <ul style={{ margin: 0, paddingLeft: \"1.25rem\" }}>\n {preferenceList.map((p) => (\n <li key={p.id ?? p.name}>{p.name ?? p.id ?? \"\"}</li>\n ))}\n </ul>\n <p>\n Trước khi bạn đồng ý, chỉ những trình theo dõi hoàn toàn cần thiết mới được triển khai.\n </p>\n <p>\n Nếu bạn chọn Chấp nhận tất cả, bạn đồng ý sử dụng tất cả trình theo dõi của chúng tôi.\n </p>\n <p>\n Nếu bạn chọn Từ chối tất cả, bạn từ chối những trình theo dõi yêu cầu sự đồng ý của bạn và sẽ không có quyền truy cập vào các đề nghị hoặc nội dung được cá nhân hóa.\n </p>\n <p>\n Nếu bạn chọn Tùy chọn, bạn có thể chọn xem bạn có đồng ý sử dụng trình theo dõi trên ứng dụng của chúng tôi hay không và ở mức độ nào.\n </p>\n <p>\n Bạn có thể rút lại sự đồng ý của mình bất kỳ lúc nào bằng cách nhấp vào liên kết trong{\" \"}\n {COOKIE_POLICY_URL ? (\n <a\n href={COOKIE_POLICY_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Chính sách cookie\n </a>\n ) : (\n \"Chính sách cookie\"\n )}{\" \"}\n của chúng tôi.\n </p>\n </div>\n <div className=\"flex justify-end gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(true)}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Tùy chọn\n </button>\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Từ chối tất cả\n </button>\n <button\n type=\"button\"\n onClick={handleAcceptAll}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Chấp nhận tất cả\n </button>\n </div>\n {createConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n\n {/* Screen 2: Custom preferences modal */}\n {showCustomModal && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Trung tâm tuỳ chọn bảo mật\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 60,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && setShowCustomModal(false)}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"28rem\",\n width: \"100%\",\n maxHeight: \"90vh\",\n overflowY: \"auto\",\n padding: \"1.25rem\",\n position: \"relative\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(false)}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button>\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\", paddingRight: \"2rem\" }}\n >\n Trung tâm tuỳ chọn bảo mật\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", marginBottom: \"1rem\" }}\n >\n <p style={{ marginBottom: \"0.5rem\" }}>\n Khi bạn truy cập bất kỳ trang web nào, trang web đó có thể lưu trữ hoặc truy xuất thông tin trên trình duyệt của bạn, chủ yếu dưới dạng cookie. Thông tin này có thể liên quan đến bạn, tùy chọn của bạn hoặc thiết bị của bạn, và chủ yếu được sử dụng để trang web hoạt động như mong đợi.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Thông tin này thường không trực tiếp xác định bạn nhưng có thể mang lại trải nghiệm web được cá nhân hóa hơn. Chúng tôi tôn trọng quyền riêng tư của bạn; bạn có thể chọn không cho phép một số loại cookie. Nhấp vào tiêu đề từng danh mục để tìm hiểu thêm và thay đổi cài đặt mặc định. Việc chặn một số cookie có thể ảnh hưởng đến trải nghiệm của bạn với trang web và các dịch vụ được cung cấp.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Nếu chọn \"Không theo dõi\" khi lần đầu sử dụng TapQuest, chỉ cookie hoàn toàn cần thiết mới được sử dụng.\n </p>\n {OTHER_INFO_URL ? (\n <a\n href={OTHER_INFO_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Thông tin khác\n </a>\n ) : (\n <span style={{ color: \"#2563eb\", textDecoration: \"underline\" }}>Thông tin khác</span>\n )}\n </div>\n <h3\n className=\"consent:text-sm consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\" }}\n >\n Quản lý tuỳ chọn đồng ý\n </h3>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", marginBottom: \"1.25rem\" }}>\n {preferenceList.map((pref) => {\n const id = pref.id ?? pref.name ?? \"\";\n const checked = effectivePreferences[id];\n const isExpanded = expandedKey === id;\n const disabled = pref.is_mandatory === true;\n return (\n <div\n key={id}\n style={{\n border: \"1px solid #e5e7eb\",\n borderRadius: \"0.5rem\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n cursor: \"pointer\",\n backgroundColor: isExpanded ? \"#f9fafb\" : \"transparent\",\n }}\n onClick={() => setExpandedKey(isExpanded ? null : id)}\n >\n <span\n style={{\n flexShrink: 0,\n width: \"1.25rem\",\n height: \"1.25rem\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"1rem\",\n color: \"#6b7280\",\n transform: isExpanded ? \"rotate(45deg)\" : \"none\",\n transition: \"transform 0.2s\",\n }}\n >\n +\n </span>\n <span\n className=\"consent:font-medium consent:text-gray-900\"\n style={{ flex: 1, fontSize: \"0.875rem\", fontWeight: 500, color: \"#111827\" }}\n >\n {pref.name ?? id}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${pref.name ?? id} ${checked ? \"bật\" : \"tắt\"}`}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation();\n togglePreference(id);\n }}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: disabled ? \"default\" : \"pointer\",\n backgroundColor: disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: disabled ? 0.7 : 1,\n transition: \"background-color 0.2s\",\n }}\n >\n <span\n style={{\n display: \"block\",\n width: 20,\n height: 20,\n borderRadius: \"50%\",\n backgroundColor: \"#fff\",\n marginLeft: checked ? 22 : 2,\n marginTop: 2,\n transition: \"margin-left 0.2s\",\n boxShadow: \"0 1px 2px rgb(0 0 0 / 0.2)\",\n }}\n />\n </button>\n </div>\n {isExpanded && (\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#4b5563\",\n padding: \"0.75rem 0.75rem 0.75rem 2.5rem\",\n borderTop: \"1px solid #e5e7eb\",\n }}\n >\n {pref.description ?? \"\"}\n </div>\n )}\n </div>\n );\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleSaveCustom}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n width: \"100%\",\n }}\n >\n Xác nhận lựa chọn của tôi\n </button>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Browser cookie helpers for phygital-consent.\n * Cookie name for consent preference: phygital_cookie_consent.\n * Stored value: { deviceId: string, selected_preferences: string[] } (JSON).\n * Default: 1 year, path /, SameSite Lax.\n */\n\nexport const COOKIE_CONSENT_NAME = \"phygital_cookie_consent\";\n\nconst ONE_YEAR_SECONDS = 365 * 24 * 60 * 60;\n\nexport interface SetCookieOptions {\n maxAge?: number;\n path?: string;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n secure?: boolean;\n}\n\n/**\n * Get a cookie value by name.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(new RegExp(\"(?:^|; )\" + encodeURIComponent(name) + \"=([^;]*)\"));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Set a cookie.\n */\nexport function setCookie(\n name: string,\n value: string,\n options: SetCookieOptions = {}\n): void {\n if (typeof document === \"undefined\") return;\n const {\n maxAge = ONE_YEAR_SECONDS,\n path = \"/\",\n sameSite = \"Lax\",\n secure = false,\n } = options;\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=${path}; max-age=${maxAge}; SameSite=${sameSite}`;\n if (secure) cookie += \"; Secure\";\n document.cookie = cookie;\n}\n\nexport interface ConsentPreferenceValue {\n deviceId: string;\n selected_preferences: string[];\n}\n\n/**\n * Read consent preference from cookie. Returns null if missing or invalid.\n */\nexport function getConsentPreferenceCookie(): ConsentPreferenceValue | null {\n const raw = getCookie(COOKIE_CONSENT_NAME);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"deviceId\" in parsed &&\n \"selected_preferences\" in parsed &&\n typeof (parsed as ConsentPreferenceValue).deviceId === \"string\" &&\n Array.isArray((parsed as ConsentPreferenceValue).selected_preferences)\n ) {\n return parsed as ConsentPreferenceValue;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\n/**\n * Store consent preference in cookie (hashed device id + selected_preferences).\n * Uses 1 year max-age and SameSite Lax.\n */\nexport function setConsentPreferenceCookie(\n deviceId: string,\n selected_preferences: string[],\n options?: Partial<SetCookieOptions>\n): void {\n const value: ConsentPreferenceValue = { deviceId, selected_preferences };\n setCookie(COOKIE_CONSENT_NAME, JSON.stringify(value), {\n maxAge: ONE_YEAR_SECONDS,\n path: \"/\",\n sameSite: \"Lax\",\n ...options,\n });\n}\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { sha256 } from \"../helpers/sha256\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\n/** Query keys for consent API */\nexport const consentQueryKeys = {\n all: [\"consent\"] as const,\n health: () => [...consentQueryKeys.all, \"health\"] as const,\n list: (params?: GetManyConsentParams) =>\n [...consentQueryKeys.all, \"list\", params] as const,\n detail: (id: string) => [...consentQueryKeys.all, \"detail\", id] as const,\n latestByUserId: (user_id: string) =>\n [...consentQueryKeys.all, \"user-id\", user_id] as const,\n latestCookieByDeviceId: (device_id: string) =>\n [...consentQueryKeys.all, \"cookies\", \"device-id\", device_id] as const,\n};\n\n// --- Queries ---\n\n/** Health check – GET /health */\nexport const useHealth = (\n options?: UseQueryOptions<HealthResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<HealthResponse>({\n queryKey: consentQueryKeys.health(),\n queryFn: () => service.getHealth(),\n ...options,\n });\n};\n\n/** Get all consent logs – GET /consent/v1/consent */\nexport const useManyConsents = (\n params?: GetManyConsentParams,\n options?: UseQueryOptions<GetManyConsentResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetManyConsentResponse>({\n queryKey: consentQueryKeys.list(params),\n queryFn: () => service.getManyConsents(params),\n ...options,\n });\n};\n\n/** Get consent log by ID – GET /consent/v1/consent/{id} */\nexport const useConsentById = (\n params: GetConsentByIdParams,\n options?: UseQueryOptions<GetConsentByIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetConsentByIdResponse>({\n queryKey: consentQueryKeys.detail(params.id),\n queryFn: () => service.getConsentById(params),\n enabled: !!params.id,\n ...options,\n });\n};\n\n/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */\nexport const useLatestConsentByUserId = (\n params: GetLatestConsentByUserIdParams,\n options?: UseQueryOptions<GetLatestConsentByUserIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestConsentByUserIdResponse>({\n queryKey: consentQueryKeys.latestByUserId(params.user_id),\n queryFn: async () => {\n const user_id = await sha256(params.user_id);\n return service.getLatestConsentByUserId({ user_id });\n },\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */\nexport const useLatestCookieConsentByDeviceId = (\n params: GetLatestCookieConsentByDeviceIdParams,\n options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestCookieConsentByDeviceIdResponse>({\n queryKey: consentQueryKeys.latestCookieByDeviceId(params.device_id),\n queryFn: async () => {\n const device_id = await sha256(params.device_id);\n return service.getLatestCookieConsentByDeviceId({ device_id });\n },\n enabled: !!params.device_id,\n ...options,\n });\n};\n\n// --- Mutations ---\n\n/** Create user consent (account register) – POST /consent/v1/user-id */\nexport const useCreateUserConsent = (\n options?: UseMutationOptions<\n CreateUserConsentResponse,\n Error,\n CreateUserConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>({\n mutationFn: async (body: CreateUserConsentUpsertDTO) => {\n const payload = { ...body };\n if (body.user_id != null && body.user_id !== \"\") {\n payload.user_id = await sha256(body.user_id);\n }\n if (body.device_id != null && body.device_id !== \"\") {\n payload.device_id = await sha256(body.device_id);\n }\n return service.createUserConsent(payload);\n },\n onSuccess: (\n _data: CreateUserConsentResponse,\n variables: CreateUserConsentUpsertDTO\n ) => {\n if (variables.user_id) {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestByUserId(variables.user_id),\n });\n }\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n\n/** Create device cookie consent – POST /consent/v1/cookies/device-id */\nexport const useCreateDeviceCookieConsent = (\n options?: UseMutationOptions<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >({\n mutationFn: async (body: CreateDeviceCookieConsentUpsertDTO) => {\n const payload = {\n ...body,\n device_id: await sha256(body.device_id),\n };\n return service.createDeviceCookieConsent(payload);\n },\n onSuccess: (\n _data: CreateDeviceCookieConsentResponse,\n variables: CreateDeviceCookieConsentUpsertDTO\n ) => {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestCookieByDeviceId(variables.device_id),\n });\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n","import { AxiosInstance } from \"axios\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n GetPoliciesParams,\n GetPoliciesResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\nconst baseUrl = \"\";\n\nexport const consentService = (axiosInstance: AxiosInstance) => {\n return {\n /**\n * Health check – GET /health\n */\n getHealth: async (): Promise<HealthResponse> => {\n const { data } = await axiosInstance.get<HealthResponse>(\"/health\");\n return data;\n },\n\n /**\n * Get all consent logs – GET /consent/v1/consent\n * @param params - Optional filter by policy type (ACCOUNT_REGISTER | COOKIE_PREFERENCE)\n */\n getManyConsents: async (\n params?: GetManyConsentParams\n ): Promise<GetManyConsentResponse> => {\n const { data } = await axiosInstance.get<GetManyConsentResponse>(\n `${baseUrl}/consent`,\n { params: params ? { type: params.type } : undefined }\n );\n return data;\n },\n\n /**\n * Get consent log by ID – GET /consent/v1/consent/{id}\n */\n getConsentById: async ({\n id,\n }: GetConsentByIdParams): Promise<GetConsentByIdResponse> => {\n const { data } = await axiosInstance.get<GetConsentByIdResponse>(\n `${baseUrl}/consent/${id}`\n );\n return data;\n },\n\n /**\n * Get policies – GET /consent/v1/policy\n * Optional filters: type (ACCOUNT_REGISTER | COOKIE_PREFERENCE), version, status (ACTIVE | INACTIVE)\n */\n getPolicies: async (params?: GetPoliciesParams): Promise<GetPoliciesResponse> => {\n const { data } = await axiosInstance.get<GetPoliciesResponse>(\n `${baseUrl}/policy`,\n { params: params ?? undefined }\n );\n return data;\n },\n\n /**\n * Get latest consent for a user – GET /consent/v1/user-id?user_id=\n */\n getLatestConsentByUserId: async ({\n user_id,\n }: GetLatestConsentByUserIdParams): Promise<GetLatestConsentByUserIdResponse> => {\n const { data } = await axiosInstance.get<GetLatestConsentByUserIdResponse>(\n `${baseUrl}/user-id`,\n { params: { user_id } }\n );\n return data;\n },\n\n /**\n * Create user consent (account register) – POST /consent/v1/user-id\n * Consumer app must hash user_id/device_id (e.g. SHA-256) before passing.\n */\n createUserConsent: async (\n body: CreateUserConsentUpsertDTO\n ): Promise<CreateUserConsentResponse> => {\n const { data } = await axiosInstance.post<CreateUserConsentResponse>(\n `${baseUrl}/user-id`,\n body\n );\n return data;\n },\n\n /**\n * Get latest cookie consent for a device – GET /consent/v1/cookies/device-id?device_id=\n */\n getLatestCookieConsentByDeviceId: async ({\n device_id,\n }: GetLatestCookieConsentByDeviceIdParams): Promise<GetLatestCookieConsentByDeviceIdResponse> => {\n const { data } =\n await axiosInstance.get<GetLatestCookieConsentByDeviceIdResponse>(\n `${baseUrl}/cookies/device-id`,\n { params: { device_id } }\n );\n return data;\n },\n\n /**\n * Create device cookie consent – POST /consent/v1/cookies/device-id\n * Consumer app must hash device_id (e.g. SHA-256) before passing.\n */\n createDeviceCookieConsent: async (\n body: CreateDeviceCookieConsentUpsertDTO\n ): Promise<CreateDeviceCookieConsentResponse> => {\n const { data } =\n await axiosInstance.post<CreateDeviceCookieConsentResponse>(\n `${baseUrl}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Input: string, encoded as UTF-8 before hashing.\n * Output: lowercase hex string.\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n */\n\n/** Encode hash digest bytes as lowercase hex string. */\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .toLowerCase();\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n * Input encoded as UTF-8; output lowercase hex.\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const utf8 = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", utf8);\n return arrayBufferToHex(hashBuffer);\n}\n\n/**\n * Pure-JS SHA-256 fallback for environments without crypto.subtle\n * (e.g. old browsers, insecure context, Node < 19).\n * Input encoded as UTF-8; output lowercase hex.\n */\nfunction sha256Fallback(value: string): string {\n const bytes = utf8Encode(value);\n const K = getK();\n const H = getH();\n const W = new Uint32Array(64);\n const numBlocks = bytes.length >> 6;\n\n for (let block = 0; block < numBlocks; block++) {\n const start = block << 6;\n for (let i = 0; i < 16; i++) {\n const o = start + (i << 2);\n W[i] =\n (bytes[o]! << 24) |\n (bytes[o + 1]! << 16) |\n (bytes[o + 2]! << 8) |\n bytes[o + 3]!;\n }\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(W[i - 15]!, 7) ^ rotr(W[i - 15]!, 18) ^ (W[i - 15]! >>> 3);\n const s1 = rotr(W[i - 2]!, 17) ^ rotr(W[i - 2]!, 19) ^ (W[i - 2]! >>> 10);\n W[i] = (W[i - 16]! + s0 + W[i - 7]! + s1) >>> 0;\n }\n let a = H[0]!,\n b = H[1]!,\n c = H[2]!,\n d = H[3]!,\n e = H[4]!,\n f = H[5]!,\n g = H[6]!,\n h = H[7]!;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i]! + W[i]!) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0]! + a) >>> 0;\n H[1] = (H[1]! + b) >>> 0;\n H[2] = (H[2]! + c) >>> 0;\n H[3] = (H[3]! + d) >>> 0;\n H[4] = (H[4]! + e) >>> 0;\n H[5] = (H[5]! + f) >>> 0;\n H[6] = (H[6]! + g) >>> 0;\n H[7] = (H[7]! + h) >>> 0;\n }\n\n let out = \"\";\n for (let i = 0; i < 8; i++) {\n const v = H[i]!;\n out +=\n (v >>> 28).toString(16) +\n ((v >>> 24) & 0xf).toString(16) +\n ((v >>> 20) & 0xf).toString(16) +\n ((v >>> 16) & 0xf).toString(16) +\n ((v >>> 12) & 0xf).toString(16) +\n ((v >>> 8) & 0xf).toString(16) +\n ((v >>> 4) & 0xf).toString(16) +\n (v & 0xf).toString(16);\n }\n return out.toLowerCase();\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\n/** Encode string to UTF-8 bytes (with SHA-256 padding for block processing). */\nfunction utf8Encode(s: string): Uint8Array {\n const n = s.length;\n const bytes: number[] = [];\n for (let i = 0; i < n; i++) {\n let c = s.charCodeAt(i);\n if (c < 0x80) {\n bytes.push(c);\n } else if (c < 0x800) {\n bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));\n } else if (c < 0xd800 || c >= 0xe000) {\n bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));\n } else {\n c = 0x10000 + (((c & 0x3ff) << 10) | (s.charCodeAt(++i) & 0x3ff));\n bytes.push(\n 0xf0 | (c >> 18),\n 0x80 | ((c >> 12) & 0x3f),\n 0x80 | ((c >> 6) & 0x3f),\n 0x80 | (c & 0x3f)\n );\n }\n }\n const len = bytes.length;\n const pad = (64 - ((len + 9) % 64)) % 64;\n const total = len + 9 + pad;\n const out = new Uint8Array(total);\n out.set(bytes);\n out[len] = 0x80;\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n view.setUint32(total - 8, 0, false);\n view.setUint32(total - 4, (len * 8) >>> 0, false);\n return out;\n}\n\nfunction getK(): number[] {\n const k: number[] = [];\n const hex =\n \"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2\";\n hex.split(\" \").forEach((h) => k.push(parseInt(h, 16)));\n return k;\n}\n\nfunction getH(): number[] {\n return [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n ];\n}\n\nfunction hasWebCrypto(): boolean {\n if (typeof crypto === \"undefined\" || !crypto.subtle) return false;\n return true;\n}\n\n/**\n * Hash a string with SHA-256.\n * Input: encoded as UTF-8. Output: lowercase hex string.\n * Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.\n * Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).\n */\nexport async function sha256(value: string): Promise<string> {\n if (typeof value !== \"string\" || value.length === 0) {\n return value;\n }\n if (hasWebCrypto()) {\n return sha256WebCrypto(value);\n }\n return Promise.resolve(sha256Fallback(value));\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientConfig, QueryClientProvider } from \"@tanstack/react-query\";\nimport axios, {\n AxiosInstance,\n AxiosRequestConfig,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ConsentService {\n consentApi: AxiosInstance;\n queryClient: QueryClient;\n updateHeaders: (headers: Record<string, string>) => void;\n}\n\nconst ConsentServiceContext = createContext<ConsentService | undefined>(undefined);\n\ninterface RequestInterceptor {\n onFulfilled?: (\n config: InternalAxiosRequestConfig\n ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;\n onRejected?: (error: unknown) => unknown;\n}\n\ninterface ResponseInterceptor {\n onFulfilled?: (\n response: AxiosResponse\n ) => AxiosResponse | Promise<AxiosResponse>;\n onRejected?: (error: unknown) => unknown;\n}\n\nexport interface ConsentServiceProviderProps {\n children: React.ReactNode;\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n queryClient?: QueryClient;\n queryClientConfig?: Partial<QueryClientConfig>;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\ninterface CreateConsentServiceParams {\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\nexport const createConsentService = ({\n baseURL = \"\",\n axiosConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}: CreateConsentServiceParams) => {\n const instance = axios.create({\n baseURL,\n ...axiosConfig,\n });\n\n instance.interceptors.request.use(\n requestInterceptors.onFulfilled,\n requestInterceptors.onRejected\n );\n\n instance.interceptors.response.use(\n responseInterceptors.onFulfilled,\n responseInterceptors.onRejected\n );\n\n const updateHeaders = (headers: Record<string, string>) => {\n Object.entries(headers).forEach(([key, value]) => {\n instance.defaults.headers.common[key] = value;\n });\n };\n\n return {\n consentApi: instance,\n updateHeaders,\n };\n};\n\nexport const ConsentServiceProvider: React.FC<ConsentServiceProviderProps> = ({\n children,\n baseURL = \"\",\n axiosConfig = {},\n queryClient,\n queryClientConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}) => {\n const queryClientInstance = useMemo(\n () =>\n queryClient ||\n new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n ...queryClientConfig?.defaultOptions?.queries,\n },\n mutations: {\n ...queryClientConfig?.defaultOptions?.mutations,\n },\n },\n queryCache: queryClientConfig?.queryCache,\n mutationCache: queryClientConfig?.mutationCache,\n }),\n [queryClient]\n );\n\n const consentService = useMemo(() => {\n const service = createConsentService({\n baseURL,\n axiosConfig,\n requestInterceptors,\n responseInterceptors,\n });\n\n return {\n consentApi: service.consentApi,\n updateHeaders: service.updateHeaders,\n queryClient: queryClientInstance,\n };\n }, [baseURL, queryClientInstance, axiosConfig, requestInterceptors, responseInterceptors]);\n\n return (\n <ConsentServiceContext.Provider value={consentService}>\n <QueryClientProvider client={queryClientInstance}>\n {children}\n </QueryClientProvider>\n </ConsentServiceContext.Provider>\n );\n};\n\nexport const useConsentService = () => {\n const context = useContext(ConsentServiceContext);\n if (!context) {\n throw new Error(\"useConsentService must be used within a ConsentServiceProvider\");\n }\n return context;\n};\n","import {\n useQuery,\n UseQueryOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetPoliciesParams,\n GetPoliciesResponse,\n} from \"../types\";\n\n/** Query keys for policy API */\nexport const policyQueryKeys = {\n all: [\"policy\"] as const,\n list: (params?: GetPoliciesParams) =>\n [...policyQueryKeys.all, \"list\", params] as const,\n};\n\n/** Get policies – GET /consent/v1/policy (optional filters: type, version, status) */\nexport const usePolicies = (\n params?: GetPoliciesParams,\n options?: UseQueryOptions<GetPoliciesResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetPoliciesResponse>({\n queryKey: policyQueryKeys.list(params),\n queryFn: () => service.getPolicies(params),\n ...options,\n });\n};\n","/**\n * Analytics and advertising consent helpers.\n * Cookie preference must exist and include \"analytics\" or \"advertising\" respectively.\n */\n\nimport { getConsentPreferenceCookie } from \"./cookie\";\n\nconst ANALYTICS_PREFERENCE_KEY = \"analytics\";\nconst ADVERTISING_PREFERENCE_KEY = \"advertising\";\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"analytics\" is in selected_preferences. Otherwise GA/GTM/PostHog/OpenReplay are blocked.\n */\nexport function isAnalyticsAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ANALYTICS_PREFERENCE_KEY);\n}\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"advertising\" is in selected_preferences. Otherwise ad tools (e.g. Google AdSense) are blocked.\n */\nexport function isAdvertisingAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ADVERTISING_PREFERENCE_KEY);\n}\n","\"use client\";\n\nimport { isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\n/**\n * Returns whether analytics (GA) is allowed based on cookie preference.\n * Re-renders when consent is updated (e.g. after banner submit).\n * Must be used within PhygitalConsentProvider.\n */\nexport function useIsAnalyticsAllowed(): boolean {\n usePhygitalConsent(); // subscribe to re-renders when refreshConsentPreference is called\n return isAnalyticsAllowed();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useCreateUserConsent } from \"../hooks/useConsent\";\n\nconst POLICY_PLACEHOLDER = `\nTanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.\n`.trim();\n\nconst REFUSAL_MESSAGE =\n \"Vì bạn không đồng ý với thỏa thuận này. Chúng tôi sẽ xóa tài khoản trong vòng x ngày. Nhấn nút 'Đồng ý' để tiếp tục\";\n\nexport interface PolicyPopupProps {\n open: boolean;\n onClose: () => void;\n onReject: () => void;\n userId: string;\n /** Called after user agrees and consent is created successfully. */\n onAgreeSuccess?: () => void;\n}\n\nexport function PolicyPopup({\n open,\n onClose,\n onReject,\n userId,\n onAgreeSuccess,\n}: PolicyPopupProps) {\n const [agreed, setAgreed] = useState(false);\n const [showRefusalAlert, setShowRefusalAlert] = useState(false);\n\n const createUserConsent = useCreateUserConsent({\n onSuccess: (_data, variables) => {\n if (variables.status === \"ACCEPTED\") onAgreeSuccess?.();\n handleClose();\n },\n });\n\n const handleClose = () => {\n setAgreed(false);\n setShowRefusalAlert(false);\n onClose();\n };\n\n const handleRefuse = () => {\n setShowRefusalAlert(true);\n };\n\n const handleBackToPolicy = () => {\n setShowRefusalAlert(false);\n };\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\") => ({\n user_id: userId,\n status,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const handleConfirmReject = () => {\n createUserConsent.mutate(getConsentPayload(\"REJECTED\"));\n onReject?.();\n };\n\n const handleAgree = () => {\n if (!agreed) return;\n createUserConsent.mutate(getConsentPayload(\"ACCEPTED\"));\n };\n\n if (!open) return null;\n\n return (\n <>\n {/* Policy modal */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n }}\n >\n <div\n className=\"consent:overflow-hidden consent:flex consent:flex-col\"\n style={{\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button> */}\n <h2\n style={{\n fontSize: \"18px\",\n fontWeight: 600,\n color: \"#000\",\n padding: \"20px 2.5rem 12px 20px\",\n }}\n >\n Chính sách bảo mật\n </h2>\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: \"1rem 1.25rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n lineHeight: 1.6,\n }}\n >\n <p style={{ margin: 0, whiteSpace: \"pre-wrap\" }}>\n {POLICY_PLACEHOLDER}\n </p>\n </div>\n <div\n style={{\n padding: \"1rem 1.25rem 1.25rem 1.25rem\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={agreed}\n onChange={(e) => setAgreed(e.target.checked)}\n style={{ width: \"1rem\", height: \"1rem\", accentColor: \"#111827\" }}\n aria-label=\"Tôi đồng ý với chính sách bảo mật trên\"\n />\n <span>Tôi đồng ý với chính sách bảo mật trên</span>\n </label>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleRefuse}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Từ chối\n </button>\n <button\n type=\"button\"\n onClick={handleAgree}\n disabled={!agreed || createUserConsent.isPending}\n className=\"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: agreed ? \"#111827\" : \"#e5e7eb\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: agreed ? \"#fff\" : \"#9ca3af\",\n border: \"none\",\n cursor: agreed ? \"pointer\" : \"not-allowed\",\n }}\n >\n Tiếp tục\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: 0 }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Refusal alert modal */}\n {showRefusalAlert && (\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Thông báo từ chối\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 70,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && handleBackToPolicy()}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"22rem\",\n width: \"100%\",\n padding: \"1.25rem\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <p\n className=\"consent:text-sm consent:text-gray-700\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#374151\",\n margin: \"0 0 1rem 0\",\n lineHeight: 1.5,\n }}\n >\n {REFUSAL_MESSAGE}\n </p>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleBackToPolicy}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Trở lại\n </button>\n <button\n type=\"button\"\n onClick={handleConfirmReject}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: \"#a71f24\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Đồng ý\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: \"0.5rem 0 0 0\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","COOKIE_CONSENT_NAME","CookieConsentBanner","EntityStatus","MediaType","PhygitalConsentProvider","PolicyPopup","consentQueryKeys","consentService","getConsentPreferenceCookie","getCookie","isAdvertisingAllowed","isAnalyticsAllowed","policyQueryKeys","setConsentPreferenceCookie","setCookie","sha256","useConsentById","useCreateDeviceCookieConsent","useCreateUserConsent","useHealth","useIsAnalyticsAllowed","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useManyConsents","usePhygitalConsent","usePolicies","__toCommonJS","MediaType","EntityStatus","import_react","import_react","COOKIE_CONSENT_NAME","getCookie","name","match","setCookie","value","options","maxAge","path","sameSite","secure","cookie","getConsentPreferenceCookie","raw","COOKIE_CONSENT_NAME","parsed","setConsentPreferenceCookie","deviceId","selected_preferences","import_react_query","baseUrl","consentService","axiosInstance","data","params","id","user_id","body","device_id","arrayBufferToHex","buffer","b","sha256WebCrypto","value","utf8","hashBuffer","sha256Fallback","bytes","utf8Encode","K","getK","H","getH","W","numBlocks","block","start","i","o","s0","rotr","s1","a","c","d","e","f","g","h","S1","ch","t1","S0","maj","t2","out","v","n","s","len","pad","total","view","k","hasWebCrypto","sha256","import_react_query","import_axios","import_react","import_jsx_runtime","ConsentServiceContext","createConsentService","baseURL","axiosConfig","requestInterceptors","responseInterceptors","instance","axios","headers","key","value","ConsentServiceProvider","children","queryClient","queryClientConfig","queryClientInstance","consentService","service","useConsentService","context","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useManyConsents","useConsentById","useLatestConsentByUserId","sha256","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","body","payload","_data","variables","useCreateDeviceCookieConsent","import_react_query","policyQueryKeys","params","usePolicies","options","consentApi","useConsentService","service","consentService","import_jsx_runtime","CookieConsentBanner","onSubmitted","onDismiss","className","dismissed","setDismissed","showCustomModal","setShowCustomModal","expandedKey","setExpandedKey","preferences","setPreferences","policyResponse","usePolicies","preferenceList","p","effectivePreferences","id","deviceId","usePhygitalConsent","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","getConsentPayload","status","selected_preferences","togglePreference","prev","handleReject","handleAcceptAll","ids","handleSaveCustom","selected","e","pref","checked","isExpanded","disabled","ANALYTICS_PREFERENCE_KEY","ADVERTISING_PREFERENCE_KEY","isAnalyticsAllowed","stored","getConsentPreferenceCookie","isAdvertisingAllowed","import_jsx_runtime","PhygitalConsentContext","PhygitalConsentProviderInner","children","deviceId","showBannerWhenNoPreference","hasConsentPreference","setHasConsentPreference","refreshConsentPreference","stored","getConsentPreferenceCookie","value","isAnalyticsAllowed","isAdvertisingAllowed","CookieConsentBanner","PhygitalConsentProvider","props","consentServiceProps","ConsentServiceProvider","usePhygitalConsent","context","useIsAnalyticsAllowed","usePhygitalConsent","isAnalyticsAllowed","import_react","import_jsx_runtime","POLICY_PLACEHOLDER","REFUSAL_MESSAGE","PolicyPopup","open","onClose","onReject","userId","onAgreeSuccess","agreed","setAgreed","showRefusalAlert","setShowRefusalAlert","createUserConsent","useCreateUserConsent","_data","variables","handleClose","handleRefuse","handleBackToPolicy","getConsentPayload","status","handleConfirmReject","handleAgree","e"]}
1
+ {"version":3,"sources":["../index.ts","../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/components/CookieConsentBanner.tsx","../src/helpers/cookie.ts","../src/hooks/useConsent.ts","../src/api/consent.ts","../src/helpers/sha256.ts","../src/provider/ConsentServiceProvider.tsx","../src/hooks/usePolicy.ts","../src/helpers/analytics-consent.ts","../src/hooks/useAnalyticsConsent.ts","../src/components/PolicyPopup.tsx"],"sourcesContent":["export * from \"./src\";\n","export enum MediaType {\n IMAGE = \"image\",\n VIDEO = \"video\",\n}\n\nexport type DateTimeNumber = number;\n\nexport interface Media {\n url: string;\n type: MediaType | string;\n thumbnail_url?: string;\n}\n\nexport enum EntityStatus {\n ACTIVE = \"Active\",\n INACTIVE = \"Inactive\",\n}\n\nexport interface CommonModel {\n id: string;\n status: EntityStatus;\n created_at: DateTimeNumber;\n updated_at: DateTimeNumber;\n created_by?: string;\n updated_by?: string;\n}\n","\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useState } from \"react\";\nimport { CookieConsentBanner } from \"../components/CookieConsentBanner\";\nimport { isAdvertisingAllowed, isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n deviceId: string;\n hasConsentPreference: boolean;\n isAnalyticsAllowed: boolean;\n isAdvertisingAllowed: boolean;\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n deviceId: string;\n children: React.ReactNode;\n showBannerWhenNoPreference?: boolean;\n}\n\n/** Inner provider: runs inside ConsentServiceProvider so useLatestConsentByUserId has context. */\nfunction PhygitalConsentProviderInner({\n children,\n deviceId,\n showBannerWhenNoPreference = true,\n}: Pick<PhygitalConsentProviderProps, \"children\" | \"deviceId\" | \"showBannerWhenNoPreference\">) {\n const [hasConsentPreference, setHasConsentPreference] = useState(false);\n\n const refreshConsentPreference = useCallback(() => {\n const stored = getConsentPreferenceCookie();\n setHasConsentPreference(!!stored);\n }, []);\n\n useEffect(() => {\n refreshConsentPreference();\n }, [refreshConsentPreference]);\n\n const value: PhygitalConsentContextValue = {\n deviceId,\n hasConsentPreference,\n isAnalyticsAllowed: isAnalyticsAllowed(),\n isAdvertisingAllowed: isAdvertisingAllowed(),\n refreshConsentPreference,\n };\n\n return (\n <PhygitalConsentContext.Provider value={value}>\n {children}\n {showBannerWhenNoPreference && !hasConsentPreference && (\n <CookieConsentBanner onSubmitted={refreshConsentPreference} />\n )}\n </PhygitalConsentContext.Provider>\n );\n}\n\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, deviceId, showBannerWhenNoPreference = true, ...consentServiceProps } = props;\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentProviderInner\n deviceId={deviceId}\n showBannerWhenNoPreference={showBannerWhenNoPreference}\n >\n {children}\n </PhygitalConsentProviderInner>\n </ConsentServiceProvider>\n );\n}\n\nexport function usePhygitalConsent() {\n const context = useContext(PhygitalConsentContext);\n if (!context) {\n throw new Error(\"usePhygitalConsent must be used within a PhygitalConsentProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { COOKIE_POLICY_URL, OTHER_INFO_URL } from \"../env/constant\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\nimport { usePolicies } from \"../hooks/usePolicy\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\nexport interface CookieConsentBannerProps {\n /** Called after consent is submitted successfully (Reject or Accept). */\n onSubmitted?: () => void;\n /** Called when the banner is dismissed (e.g. hide from UI). */\n onDismiss?: () => void;\n /** Optional class name for the root container. */\n className?: string;\n}\n\nexport function CookieConsentBanner({\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [showCustomModal, setShowCustomModal] = useState(false);\n const [expandedKey, setExpandedKey] = useState<string | null>(null);\n const [preferences, setPreferences] = useState<Record<string, boolean>>({});\n\n const { data: policyResponse } = usePolicies({ type: \"COOKIE_PREFERENCE\", status: \"ACTIVE\" });\n\n const policy = useMemo(\n () => {\n if (!policyResponse?.data) return null;\n\n return policyResponse?.data?.find((p) => p.type === \"COOKIE_PREFERENCE\" && p.status === \"ACTIVE\") ?? policyResponse?.data?.[0];\n },\n [policyResponse]\n );\n const preferenceList = policy?.preferences ?? [];\n\n const effectivePreferences = useMemo(() => {\n const defaultAllTrue = Object.fromEntries(\n preferenceList.map((p) => [p.id ?? \"\", true]).filter(([id]) => !!id)\n );\n return { ...defaultAllTrue, ...preferences };\n }, [preferenceList, preferences]);\n\n const { deviceId } = usePhygitalConsent();\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n setShowCustomModal(false);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n preferenceList\n .map((p) => p.id)\n .filter((id): id is string => !!id)\n .filter((id) => effectivePreferences[id]);\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\", selected_preferences: string[]) => ({\n device_id: deviceId,\n status,\n selected_preferences,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const togglePreference = (id: string) => {\n const pref = preferenceList.find((p) => p.id === id);\n if (pref?.is_mandatory) return;\n setPreferences((prev) => ({ ...prev, [id]: !effectivePreferences[id] }));\n };\n\n const handleReject = () => {\n createConsent.mutate(getConsentPayload(\"REJECTED\", []));\n };\n\n const handleAcceptAll = () => {\n const ids = preferenceList.map((p) => p.id).filter((id): id is string => !!id);\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", ids.length > 0 ? ids : []));\n };\n\n const handleSaveCustom = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", selected.length > 0 ? selected : [\"essential\"]));\n };\n\n if (dismissed) return null;\n\n return (\n <>\n {/* Screen 1: Bottom banner */}\n <div\n role=\"dialog\"\n aria-label=\"Thông báo về cookie\"\n className={`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${className}`}\n style={{\n position: \"fixed\",\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 50,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n borderRadius: \"0.75rem 0.75rem 0 0\",\n borderWidth: \"1px 1px 0 1px\",\n borderColor: \"#e5e7eb\",\n backgroundColor: \"#fff\",\n padding: \"1rem\",\n boxShadow: \"0 -4px 6px -1px rgb(0 0 0 / 0.1)\",\n }}\n >\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\" }}\n >\n Thông báo về cookie\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\" }}\n >\n <p>\n TapQuest sử dụng trình theo dõi (như Cookie hoặc SDK) cho:\n </p>\n <ul style={{ margin: 0, paddingLeft: \"1.25rem\" }}>\n {preferenceList.map((p) => (\n <li key={p.id ?? p.name}>{p.name ?? p.id ?? \"\"}</li>\n ))}\n </ul>\n <p>\n Trước khi bạn đồng ý, chỉ những trình theo dõi hoàn toàn cần thiết mới được triển khai.\n </p>\n <p>\n Nếu bạn chọn Chấp nhận tất cả, bạn đồng ý sử dụng tất cả trình theo dõi của chúng tôi.\n </p>\n <p>\n Nếu bạn chọn Từ chối tất cả, bạn từ chối những trình theo dõi yêu cầu sự đồng ý của bạn và sẽ không có quyền truy cập vào các đề nghị hoặc nội dung được cá nhân hóa.\n </p>\n <p>\n Nếu bạn chọn Tùy chọn, bạn có thể chọn xem bạn có đồng ý sử dụng trình theo dõi trên ứng dụng của chúng tôi hay không và ở mức độ nào.\n </p>\n <p>\n Bạn có thể rút lại sự đồng ý của mình bất kỳ lúc nào bằng cách nhấp vào liên kết trong{\" \"}\n {COOKIE_POLICY_URL ? (\n <a\n href={COOKIE_POLICY_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Chính sách cookie\n </a>\n ) : (\n \"Chính sách cookie\"\n )}{\" \"}\n của chúng tôi.\n </p>\n </div>\n <div className=\"flex justify-end gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(true)}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Tùy chọn\n </button>\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Từ chối tất cả\n </button>\n <button\n type=\"button\"\n onClick={handleAcceptAll}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Chấp nhận tất cả\n </button>\n </div>\n {createConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n\n {/* Screen 2: Custom preferences modal */}\n {showCustomModal && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Trung tâm tuỳ chọn bảo mật\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 60,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && setShowCustomModal(false)}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"28rem\",\n width: \"100%\",\n maxHeight: \"90vh\",\n overflowY: \"auto\",\n padding: \"1.25rem\",\n position: \"relative\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(false)}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button>\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\", paddingRight: \"2rem\" }}\n >\n Trung tâm tuỳ chọn bảo mật\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", marginBottom: \"1rem\" }}\n >\n <p style={{ marginBottom: \"0.5rem\" }}>\n Khi bạn truy cập bất kỳ trang web nào, trang web đó có thể lưu trữ hoặc truy xuất thông tin trên trình duyệt của bạn, chủ yếu dưới dạng cookie. Thông tin này có thể liên quan đến bạn, tùy chọn của bạn hoặc thiết bị của bạn, và chủ yếu được sử dụng để trang web hoạt động như mong đợi.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Thông tin này thường không trực tiếp xác định bạn nhưng có thể mang lại trải nghiệm web được cá nhân hóa hơn. Chúng tôi tôn trọng quyền riêng tư của bạn; bạn có thể chọn không cho phép một số loại cookie. Nhấp vào tiêu đề từng danh mục để tìm hiểu thêm và thay đổi cài đặt mặc định. Việc chặn một số cookie có thể ảnh hưởng đến trải nghiệm của bạn với trang web và các dịch vụ được cung cấp.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Nếu chọn \"Không theo dõi\" khi lần đầu sử dụng TapQuest, chỉ cookie hoàn toàn cần thiết mới được sử dụng.\n </p>\n {OTHER_INFO_URL ? (\n <a\n href={OTHER_INFO_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Thông tin khác\n </a>\n ) : (\n <span style={{ color: \"#2563eb\", textDecoration: \"underline\" }}>Thông tin khác</span>\n )}\n </div>\n <h3\n className=\"consent:text-sm consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\" }}\n >\n Quản lý tuỳ chọn đồng ý\n </h3>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", marginBottom: \"1.25rem\" }}>\n {preferenceList.map((pref) => {\n const id = pref.id ?? pref.name ?? \"\";\n const checked = effectivePreferences[id];\n const isExpanded = expandedKey === id;\n const disabled = pref.is_mandatory === true;\n return (\n <div\n key={id}\n style={{\n border: \"1px solid #e5e7eb\",\n borderRadius: \"0.5rem\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n cursor: \"pointer\",\n backgroundColor: isExpanded ? \"#f9fafb\" : \"transparent\",\n }}\n onClick={() => setExpandedKey(isExpanded ? null : id)}\n >\n <span\n style={{\n flexShrink: 0,\n width: \"1.25rem\",\n height: \"1.25rem\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"1rem\",\n color: \"#6b7280\",\n transform: isExpanded ? \"rotate(45deg)\" : \"none\",\n transition: \"transform 0.2s\",\n }}\n >\n +\n </span>\n <span\n className=\"consent:font-medium consent:text-gray-900\"\n style={{ flex: 1, fontSize: \"0.875rem\", fontWeight: 500, color: \"#111827\" }}\n >\n {pref.name ?? id}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${pref.name ?? id} ${checked ? \"bật\" : \"tắt\"}`}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation();\n togglePreference(id);\n }}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: disabled ? \"default\" : \"pointer\",\n backgroundColor: disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: disabled ? 0.7 : 1,\n transition: \"background-color 0.2s\",\n }}\n >\n <span\n style={{\n display: \"block\",\n width: 20,\n height: 20,\n borderRadius: \"50%\",\n backgroundColor: \"#fff\",\n marginLeft: checked ? 22 : 2,\n marginTop: 2,\n transition: \"margin-left 0.2s\",\n boxShadow: \"0 1px 2px rgb(0 0 0 / 0.2)\",\n }}\n />\n </button>\n </div>\n {isExpanded && (\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#4b5563\",\n padding: \"0.75rem 0.75rem 0.75rem 2.5rem\",\n borderTop: \"1px solid #e5e7eb\",\n }}\n >\n {pref.description ?? \"\"}\n </div>\n )}\n </div>\n );\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleSaveCustom}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n width: \"100%\",\n }}\n >\n Xác nhận lựa chọn của tôi\n </button>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Browser cookie helpers for phygital-consent.\n * Cookie name for consent preference: phygital_cookie_consent.\n * Stored value: { deviceId: string, selected_preferences: string[] } (JSON).\n * Default: 1 year, path /, SameSite Lax.\n */\n\nexport const COOKIE_CONSENT_NAME = \"phygital_cookie_consent\";\n\nconst ONE_YEAR_SECONDS = 365 * 24 * 60 * 60;\n\nexport interface SetCookieOptions {\n maxAge?: number;\n path?: string;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n secure?: boolean;\n}\n\n/**\n * Get a cookie value by name.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(new RegExp(\"(?:^|; )\" + encodeURIComponent(name) + \"=([^;]*)\"));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Set a cookie.\n */\nexport function setCookie(\n name: string,\n value: string,\n options: SetCookieOptions = {}\n): void {\n if (typeof document === \"undefined\") return;\n const {\n maxAge = ONE_YEAR_SECONDS,\n path = \"/\",\n sameSite = \"Lax\",\n secure = false,\n } = options;\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=${path}; max-age=${maxAge}; SameSite=${sameSite}`;\n if (secure) cookie += \"; Secure\";\n document.cookie = cookie;\n}\n\nexport interface ConsentPreferenceValue {\n deviceId: string;\n selected_preferences: string[];\n}\n\n/**\n * Read consent preference from cookie. Returns null if missing or invalid.\n */\nexport function getConsentPreferenceCookie(): ConsentPreferenceValue | null {\n const raw = getCookie(COOKIE_CONSENT_NAME);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"deviceId\" in parsed &&\n \"selected_preferences\" in parsed &&\n typeof (parsed as ConsentPreferenceValue).deviceId === \"string\" &&\n Array.isArray((parsed as ConsentPreferenceValue).selected_preferences)\n ) {\n return parsed as ConsentPreferenceValue;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\n/**\n * Store consent preference in cookie (hashed device id + selected_preferences).\n * Uses 1 year max-age and SameSite Lax.\n */\nexport function setConsentPreferenceCookie(\n deviceId: string,\n selected_preferences: string[],\n options?: Partial<SetCookieOptions>\n): void {\n const value: ConsentPreferenceValue = { deviceId, selected_preferences };\n setCookie(COOKIE_CONSENT_NAME, JSON.stringify(value), {\n maxAge: ONE_YEAR_SECONDS,\n path: \"/\",\n sameSite: \"Lax\",\n ...options,\n });\n}\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { sha256 } from \"../helpers/sha256\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\n/** Query keys for consent API */\nexport const consentQueryKeys = {\n all: [\"consent\"] as const,\n health: () => [...consentQueryKeys.all, \"health\"] as const,\n list: (params?: GetManyConsentParams) =>\n [...consentQueryKeys.all, \"list\", params] as const,\n detail: (id: string) => [...consentQueryKeys.all, \"detail\", id] as const,\n latestByUserId: (user_id: string) =>\n [...consentQueryKeys.all, \"user-id\", user_id] as const,\n latestCookieByDeviceId: (device_id: string) =>\n [...consentQueryKeys.all, \"cookies\", \"device-id\", device_id] as const,\n};\n\n// --- Queries ---\n\n/** Health check – GET /health */\nexport const useHealth = (\n options?: UseQueryOptions<HealthResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<HealthResponse>({\n queryKey: consentQueryKeys.health(),\n queryFn: () => service.getHealth(),\n ...options,\n });\n};\n\n/** Get all consent logs – GET /consent/v1/consent */\nexport const useManyConsents = (\n params?: GetManyConsentParams,\n options?: UseQueryOptions<GetManyConsentResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetManyConsentResponse>({\n queryKey: consentQueryKeys.list(params),\n queryFn: () => service.getManyConsents(params),\n ...options,\n });\n};\n\n/** Get consent log by ID – GET /consent/v1/consent/{id} */\nexport const useConsentById = (\n params: GetConsentByIdParams,\n options?: UseQueryOptions<GetConsentByIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetConsentByIdResponse>({\n queryKey: consentQueryKeys.detail(params.id),\n queryFn: () => service.getConsentById(params),\n enabled: !!params.id,\n ...options,\n });\n};\n\n/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */\nexport const useLatestConsentByUserId = (\n params: GetLatestConsentByUserIdParams,\n options?: UseQueryOptions<GetLatestConsentByUserIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestConsentByUserIdResponse>({\n queryKey: consentQueryKeys.latestByUserId(params.user_id),\n queryFn: async () => {\n const user_id = await sha256(params.user_id);\n return service.getLatestConsentByUserId({ user_id });\n },\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */\nexport const useLatestCookieConsentByDeviceId = (\n params: GetLatestCookieConsentByDeviceIdParams,\n options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestCookieConsentByDeviceIdResponse>({\n queryKey: consentQueryKeys.latestCookieByDeviceId(params.device_id),\n queryFn: async () => {\n const device_id = await sha256(params.device_id);\n return service.getLatestCookieConsentByDeviceId({ device_id });\n },\n enabled: !!params.device_id,\n ...options,\n });\n};\n\n// --- Mutations ---\n\n/** Create user consent (account register) – POST /consent/v1/user-id */\nexport const useCreateUserConsent = (\n options?: UseMutationOptions<\n CreateUserConsentResponse,\n Error,\n CreateUserConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>({\n mutationFn: async (body: CreateUserConsentUpsertDTO) => {\n const payload = { ...body };\n if (body.user_id != null && body.user_id !== \"\") {\n payload.user_id = await sha256(body.user_id);\n }\n if (body.device_id != null && body.device_id !== \"\") {\n payload.device_id = await sha256(body.device_id);\n }\n return service.createUserConsent(payload);\n },\n onSuccess: (\n _data: CreateUserConsentResponse,\n variables: CreateUserConsentUpsertDTO\n ) => {\n if (variables.user_id) {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestByUserId(variables.user_id),\n });\n }\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n\n/** Create device cookie consent – POST /consent/v1/cookies/device-id */\nexport const useCreateDeviceCookieConsent = (\n options?: UseMutationOptions<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >({\n mutationFn: async (body: CreateDeviceCookieConsentUpsertDTO) => {\n const payload = {\n ...body,\n device_id: await sha256(body.device_id),\n };\n return service.createDeviceCookieConsent(payload);\n },\n onSuccess: (\n _data: CreateDeviceCookieConsentResponse,\n variables: CreateDeviceCookieConsentUpsertDTO\n ) => {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestCookieByDeviceId(variables.device_id),\n });\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n","import { AxiosInstance } from \"axios\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n GetPoliciesParams,\n GetPoliciesResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\nconst baseUrl = \"\";\n\nexport const consentService = (axiosInstance: AxiosInstance) => {\n return {\n /**\n * Health check – GET /health\n */\n getHealth: async (): Promise<HealthResponse> => {\n const { data } = await axiosInstance.get<HealthResponse>(\"/health\");\n return data;\n },\n\n /**\n * Get all consent logs – GET /consent/v1/consent\n * @param params - Optional filter by policy type (ACCOUNT_REGISTER | COOKIE_PREFERENCE)\n */\n getManyConsents: async (\n params?: GetManyConsentParams\n ): Promise<GetManyConsentResponse> => {\n const { data } = await axiosInstance.get<GetManyConsentResponse>(\n `${baseUrl}/consent`,\n { params: params ? { type: params.type } : undefined }\n );\n return data;\n },\n\n /**\n * Get consent log by ID – GET /consent/v1/consent/{id}\n */\n getConsentById: async ({\n id,\n }: GetConsentByIdParams): Promise<GetConsentByIdResponse> => {\n const { data } = await axiosInstance.get<GetConsentByIdResponse>(\n `${baseUrl}/consent/${id}`\n );\n return data;\n },\n\n /**\n * Get policies – GET /consent/v1/policy\n * Optional filters: type (ACCOUNT_REGISTER | COOKIE_PREFERENCE), version, status (ACTIVE | INACTIVE)\n */\n getPolicies: async (params?: GetPoliciesParams): Promise<GetPoliciesResponse> => {\n const { data } = await axiosInstance.get<GetPoliciesResponse>(\n `${baseUrl}/policy`,\n { params: params ?? undefined }\n );\n return data;\n },\n\n /**\n * Get latest consent for a user – GET /consent/v1/user-id?user_id=\n */\n getLatestConsentByUserId: async ({\n user_id,\n }: GetLatestConsentByUserIdParams): Promise<GetLatestConsentByUserIdResponse> => {\n const { data } = await axiosInstance.get<GetLatestConsentByUserIdResponse>(\n `${baseUrl}/user-id`,\n { params: { user_id } }\n );\n return data;\n },\n\n /**\n * Create user consent (account register) – POST /consent/v1/user-id\n * Consumer app must hash user_id/device_id (e.g. SHA-256) before passing.\n */\n createUserConsent: async (\n body: CreateUserConsentUpsertDTO\n ): Promise<CreateUserConsentResponse> => {\n const { data } = await axiosInstance.post<CreateUserConsentResponse>(\n `${baseUrl}/user-id`,\n body\n );\n return data;\n },\n\n /**\n * Get latest cookie consent for a device – GET /consent/v1/cookies/device-id?device_id=\n */\n getLatestCookieConsentByDeviceId: async ({\n device_id,\n }: GetLatestCookieConsentByDeviceIdParams): Promise<GetLatestCookieConsentByDeviceIdResponse> => {\n const { data } =\n await axiosInstance.get<GetLatestCookieConsentByDeviceIdResponse>(\n `${baseUrl}/cookies/device-id`,\n { params: { device_id } }\n );\n return data;\n },\n\n /**\n * Create device cookie consent – POST /consent/v1/cookies/device-id\n * Consumer app must hash device_id (e.g. SHA-256) before passing.\n */\n createDeviceCookieConsent: async (\n body: CreateDeviceCookieConsentUpsertDTO\n ): Promise<CreateDeviceCookieConsentResponse> => {\n const { data } =\n await axiosInstance.post<CreateDeviceCookieConsentResponse>(\n `${baseUrl}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Input: string, encoded as UTF-8 before hashing.\n * Output: lowercase hex string.\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n */\n\n/** Encode hash digest bytes as lowercase hex string. */\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .toLowerCase();\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n * Input encoded as UTF-8; output lowercase hex.\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const utf8 = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", utf8);\n return arrayBufferToHex(hashBuffer);\n}\n\n/**\n * Pure-JS SHA-256 fallback for environments without crypto.subtle\n * (e.g. old browsers, insecure context, Node < 19).\n * Input encoded as UTF-8; output lowercase hex.\n */\nfunction sha256Fallback(value: string): string {\n const bytes = utf8Encode(value);\n const K = getK();\n const H = getH();\n const W = new Uint32Array(64);\n const numBlocks = bytes.length >> 6;\n\n for (let block = 0; block < numBlocks; block++) {\n const start = block << 6;\n for (let i = 0; i < 16; i++) {\n const o = start + (i << 2);\n W[i] =\n (bytes[o]! << 24) |\n (bytes[o + 1]! << 16) |\n (bytes[o + 2]! << 8) |\n bytes[o + 3]!;\n }\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(W[i - 15]!, 7) ^ rotr(W[i - 15]!, 18) ^ (W[i - 15]! >>> 3);\n const s1 = rotr(W[i - 2]!, 17) ^ rotr(W[i - 2]!, 19) ^ (W[i - 2]! >>> 10);\n W[i] = (W[i - 16]! + s0 + W[i - 7]! + s1) >>> 0;\n }\n let a = H[0]!,\n b = H[1]!,\n c = H[2]!,\n d = H[3]!,\n e = H[4]!,\n f = H[5]!,\n g = H[6]!,\n h = H[7]!;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i]! + W[i]!) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0]! + a) >>> 0;\n H[1] = (H[1]! + b) >>> 0;\n H[2] = (H[2]! + c) >>> 0;\n H[3] = (H[3]! + d) >>> 0;\n H[4] = (H[4]! + e) >>> 0;\n H[5] = (H[5]! + f) >>> 0;\n H[6] = (H[6]! + g) >>> 0;\n H[7] = (H[7]! + h) >>> 0;\n }\n\n let out = \"\";\n for (let i = 0; i < 8; i++) {\n const v = H[i]!;\n out +=\n (v >>> 28).toString(16) +\n ((v >>> 24) & 0xf).toString(16) +\n ((v >>> 20) & 0xf).toString(16) +\n ((v >>> 16) & 0xf).toString(16) +\n ((v >>> 12) & 0xf).toString(16) +\n ((v >>> 8) & 0xf).toString(16) +\n ((v >>> 4) & 0xf).toString(16) +\n (v & 0xf).toString(16);\n }\n return out.toLowerCase();\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\n/** Encode string to UTF-8 bytes (with SHA-256 padding for block processing). */\nfunction utf8Encode(s: string): Uint8Array {\n const n = s.length;\n const bytes: number[] = [];\n for (let i = 0; i < n; i++) {\n let c = s.charCodeAt(i);\n if (c < 0x80) {\n bytes.push(c);\n } else if (c < 0x800) {\n bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));\n } else if (c < 0xd800 || c >= 0xe000) {\n bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));\n } else {\n c = 0x10000 + (((c & 0x3ff) << 10) | (s.charCodeAt(++i) & 0x3ff));\n bytes.push(\n 0xf0 | (c >> 18),\n 0x80 | ((c >> 12) & 0x3f),\n 0x80 | ((c >> 6) & 0x3f),\n 0x80 | (c & 0x3f)\n );\n }\n }\n const len = bytes.length;\n const pad = (64 - ((len + 9) % 64)) % 64;\n const total = len + 9 + pad;\n const out = new Uint8Array(total);\n out.set(bytes);\n out[len] = 0x80;\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n view.setUint32(total - 8, 0, false);\n view.setUint32(total - 4, (len * 8) >>> 0, false);\n return out;\n}\n\nfunction getK(): number[] {\n const k: number[] = [];\n const hex =\n \"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2\";\n hex.split(\" \").forEach((h) => k.push(parseInt(h, 16)));\n return k;\n}\n\nfunction getH(): number[] {\n return [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n ];\n}\n\nfunction hasWebCrypto(): boolean {\n if (typeof crypto === \"undefined\" || !crypto.subtle) return false;\n return true;\n}\n\n/**\n * Hash a string with SHA-256.\n * Input: encoded as UTF-8. Output: lowercase hex string.\n * Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.\n * Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).\n */\nexport async function sha256(value: string): Promise<string> {\n if (typeof value !== \"string\" || value.length === 0) {\n return value;\n }\n if (hasWebCrypto()) {\n return sha256WebCrypto(value);\n }\n return Promise.resolve(sha256Fallback(value));\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientConfig, QueryClientProvider } from \"@tanstack/react-query\";\nimport axios, {\n AxiosInstance,\n AxiosRequestConfig,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ConsentService {\n consentApi: AxiosInstance;\n queryClient: QueryClient;\n updateHeaders: (headers: Record<string, string>) => void;\n}\n\nconst ConsentServiceContext = createContext<ConsentService | undefined>(undefined);\n\ninterface RequestInterceptor {\n onFulfilled?: (\n config: InternalAxiosRequestConfig\n ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;\n onRejected?: (error: unknown) => unknown;\n}\n\ninterface ResponseInterceptor {\n onFulfilled?: (\n response: AxiosResponse\n ) => AxiosResponse | Promise<AxiosResponse>;\n onRejected?: (error: unknown) => unknown;\n}\n\nexport interface ConsentServiceProviderProps {\n children: React.ReactNode;\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n queryClient?: QueryClient;\n queryClientConfig?: Partial<QueryClientConfig>;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\ninterface CreateConsentServiceParams {\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\nexport const createConsentService = ({\n baseURL = \"\",\n axiosConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}: CreateConsentServiceParams) => {\n const instance = axios.create({\n baseURL,\n ...axiosConfig,\n });\n\n instance.interceptors.request.use(\n requestInterceptors.onFulfilled,\n requestInterceptors.onRejected\n );\n\n instance.interceptors.response.use(\n responseInterceptors.onFulfilled,\n responseInterceptors.onRejected\n );\n\n const updateHeaders = (headers: Record<string, string>) => {\n Object.entries(headers).forEach(([key, value]) => {\n instance.defaults.headers.common[key] = value;\n });\n };\n\n return {\n consentApi: instance,\n updateHeaders,\n };\n};\n\nexport const ConsentServiceProvider: React.FC<ConsentServiceProviderProps> = ({\n children,\n baseURL = \"\",\n axiosConfig = {},\n queryClient,\n queryClientConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}) => {\n const queryClientInstance = useMemo(\n () =>\n queryClient ||\n new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n ...queryClientConfig?.defaultOptions?.queries,\n },\n mutations: {\n ...queryClientConfig?.defaultOptions?.mutations,\n },\n },\n queryCache: queryClientConfig?.queryCache,\n mutationCache: queryClientConfig?.mutationCache,\n }),\n [queryClient]\n );\n\n const consentService = useMemo(() => {\n const service = createConsentService({\n baseURL,\n axiosConfig,\n requestInterceptors,\n responseInterceptors,\n });\n\n return {\n consentApi: service.consentApi,\n updateHeaders: service.updateHeaders,\n queryClient: queryClientInstance,\n };\n }, [baseURL, queryClientInstance, axiosConfig, requestInterceptors, responseInterceptors]);\n\n return (\n <ConsentServiceContext.Provider value={consentService}>\n <QueryClientProvider client={queryClientInstance}>\n {children}\n </QueryClientProvider>\n </ConsentServiceContext.Provider>\n );\n};\n\nexport const useConsentService = () => {\n const context = useContext(ConsentServiceContext);\n if (!context) {\n throw new Error(\"useConsentService must be used within a ConsentServiceProvider\");\n }\n return context;\n};\n","import {\n useQuery,\n UseQueryOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetPoliciesParams,\n GetPoliciesResponse,\n} from \"../types\";\n\n/** Query keys for policy API */\nexport const policyQueryKeys = {\n all: [\"policy\"] as const,\n list: (params?: GetPoliciesParams) =>\n [...policyQueryKeys.all, \"list\", params] as const,\n};\n\n/** Get policies – GET /consent/v1/policy (optional filters: type, version, status) */\nexport const usePolicies = (\n params?: GetPoliciesParams,\n options?: UseQueryOptions<GetPoliciesResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetPoliciesResponse>({\n queryKey: policyQueryKeys.list(params),\n queryFn: () => service.getPolicies(params),\n ...options,\n });\n};\n","/**\n * Analytics and advertising consent helpers.\n * Cookie preference must exist and include \"analytics\" or \"advertising\" respectively.\n */\n\nimport { getConsentPreferenceCookie } from \"./cookie\";\n\nconst ANALYTICS_PREFERENCE_KEY = \"analytics\";\nconst ADVERTISING_PREFERENCE_KEY = \"advertising\";\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"analytics\" is in selected_preferences. Otherwise GA/GTM/PostHog/OpenReplay are blocked.\n */\nexport function isAnalyticsAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ANALYTICS_PREFERENCE_KEY);\n}\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"advertising\" is in selected_preferences. Otherwise ad tools (e.g. Google AdSense) are blocked.\n */\nexport function isAdvertisingAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ADVERTISING_PREFERENCE_KEY);\n}\n","\"use client\";\n\nimport { isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\n/**\n * Returns whether analytics (GA) is allowed based on cookie preference.\n * Re-renders when consent is updated (e.g. after banner submit).\n * Must be used within PhygitalConsentProvider.\n */\nexport function useIsAnalyticsAllowed(): boolean {\n usePhygitalConsent(); // subscribe to re-renders when refreshConsentPreference is called\n return isAnalyticsAllowed();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useCreateUserConsent } from \"../hooks/useConsent\";\n\nconst POLICY_PLACEHOLDER = `\nTanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.\n`.trim();\n\nconst REFUSAL_MESSAGE =\n \"Vì bạn không đồng ý với thỏa thuận này. Chúng tôi sẽ xóa tài khoản trong vòng x ngày. Nhấn nút 'Đồng ý' để tiếp tục\";\n\nexport interface PolicyPopupProps {\n open: boolean;\n onClose: () => void;\n onReject: () => void;\n userId: string;\n /** Called after user agrees and consent is created successfully. */\n onAgreeSuccess?: () => void;\n}\n\nexport function PolicyPopup({\n open,\n onClose,\n onReject,\n userId,\n onAgreeSuccess,\n}: PolicyPopupProps) {\n const [agreed, setAgreed] = useState(false);\n const [showRefusalAlert, setShowRefusalAlert] = useState(false);\n\n const createUserConsent = useCreateUserConsent({\n onSuccess: (_data, variables) => {\n if (variables.status === \"ACCEPTED\") onAgreeSuccess?.();\n handleClose();\n },\n });\n\n const handleClose = () => {\n setAgreed(false);\n setShowRefusalAlert(false);\n onClose();\n };\n\n const handleRefuse = () => {\n setShowRefusalAlert(true);\n };\n\n const handleBackToPolicy = () => {\n setShowRefusalAlert(false);\n };\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\") => ({\n user_id: userId,\n status,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const handleConfirmReject = () => {\n createUserConsent.mutate(getConsentPayload(\"REJECTED\"));\n onReject?.();\n };\n\n const handleAgree = () => {\n if (!agreed) return;\n createUserConsent.mutate(getConsentPayload(\"ACCEPTED\"));\n };\n\n if (!open) return null;\n\n return (\n <>\n {/* Policy modal */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <div\n className=\"consent:overflow-hidden consent:flex consent:flex-col\"\n style={{\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button> */}\n <h2\n style={{\n fontSize: \"20px\",\n fontWeight: 600,\n color: \"#000\",\n padding: \"20px 2.5rem 12px 20px\",\n }}\n >\n Chính sách bảo mật\n </h2>\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: \"1rem 1.25rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n lineHeight: 1.6,\n }}\n >\n <p style={{ margin: 0, whiteSpace: \"pre-wrap\" }}>\n {POLICY_PLACEHOLDER}\n </p>\n </div>\n <div\n style={{\n padding: \"1rem 1.25rem 1.25rem 1.25rem\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={agreed}\n onChange={(e) => setAgreed(e.target.checked)}\n style={{ width: \"1rem\", height: \"1rem\", accentColor: \"#111827\" }}\n aria-label=\"Tôi đồng ý với chính sách bảo mật trên\"\n />\n <span>Tôi đồng ý với chính sách bảo mật trên</span>\n </label>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleRefuse}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Từ chối\n </button>\n <button\n type=\"button\"\n onClick={handleAgree}\n disabled={!agreed || createUserConsent.isPending}\n className=\"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: agreed ? \"#111827\" : \"#e5e7eb\",\n padding: \"0.5rem 1rem\",\n fontSize: \"1rem\",\n fontWeight: 500,\n color: agreed ? \"#fff\" : \"#9ca3af\",\n border: \"none\",\n cursor: agreed ? \"pointer\" : \"not-allowed\",\n }}\n >\n Tiếp tục\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"1rem\", color: \"#dc2626\", margin: 0 }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Refusal alert modal */}\n {showRefusalAlert && (\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Thông báo từ chối\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 70,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && handleBackToPolicy()}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"22rem\",\n width: \"100%\",\n padding: \"1.25rem\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <p\n className=\"consent:text-sm consent:text-gray-700\"\n style={{\n fontSize: \"1rem\",\n color: \"#374151\",\n margin: \"0 0 1rem 0\",\n lineHeight: 1.5,\n }}\n >\n {REFUSAL_MESSAGE}\n </p>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleBackToPolicy}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Trở lại\n </button>\n <button\n type=\"button\"\n onClick={handleConfirmReject}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: \"#a71f24\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Đồng ý\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: \"0.5rem 0 0 0\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","COOKIE_CONSENT_NAME","CookieConsentBanner","EntityStatus","MediaType","PhygitalConsentProvider","PolicyPopup","consentQueryKeys","consentService","getConsentPreferenceCookie","getCookie","isAdvertisingAllowed","isAnalyticsAllowed","policyQueryKeys","setConsentPreferenceCookie","setCookie","sha256","useConsentById","useCreateDeviceCookieConsent","useCreateUserConsent","useHealth","useIsAnalyticsAllowed","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useManyConsents","usePhygitalConsent","usePolicies","__toCommonJS","MediaType","EntityStatus","import_react","import_react","COOKIE_CONSENT_NAME","getCookie","name","match","setCookie","value","options","maxAge","path","sameSite","secure","cookie","getConsentPreferenceCookie","raw","COOKIE_CONSENT_NAME","parsed","setConsentPreferenceCookie","deviceId","selected_preferences","import_react_query","baseUrl","consentService","axiosInstance","data","params","id","user_id","body","device_id","arrayBufferToHex","buffer","b","sha256WebCrypto","value","utf8","hashBuffer","sha256Fallback","bytes","utf8Encode","K","getK","H","getH","W","numBlocks","block","start","i","o","s0","rotr","s1","a","c","d","e","f","g","h","S1","ch","t1","S0","maj","t2","out","v","n","s","len","pad","total","view","k","hasWebCrypto","sha256","import_react_query","import_axios","import_react","import_jsx_runtime","ConsentServiceContext","createConsentService","baseURL","axiosConfig","requestInterceptors","responseInterceptors","instance","axios","headers","key","value","ConsentServiceProvider","children","queryClient","queryClientConfig","queryClientInstance","consentService","service","useConsentService","context","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useManyConsents","useConsentById","useLatestConsentByUserId","sha256","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","body","payload","_data","variables","useCreateDeviceCookieConsent","import_react_query","policyQueryKeys","params","usePolicies","options","consentApi","useConsentService","service","consentService","import_jsx_runtime","CookieConsentBanner","onSubmitted","onDismiss","className","dismissed","setDismissed","showCustomModal","setShowCustomModal","expandedKey","setExpandedKey","preferences","setPreferences","policyResponse","usePolicies","preferenceList","p","effectivePreferences","id","deviceId","usePhygitalConsent","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","getConsentPayload","status","selected_preferences","togglePreference","prev","handleReject","handleAcceptAll","ids","handleSaveCustom","selected","e","pref","checked","isExpanded","disabled","ANALYTICS_PREFERENCE_KEY","ADVERTISING_PREFERENCE_KEY","isAnalyticsAllowed","stored","getConsentPreferenceCookie","isAdvertisingAllowed","import_jsx_runtime","PhygitalConsentContext","PhygitalConsentProviderInner","children","deviceId","showBannerWhenNoPreference","hasConsentPreference","setHasConsentPreference","refreshConsentPreference","stored","getConsentPreferenceCookie","value","isAnalyticsAllowed","isAdvertisingAllowed","CookieConsentBanner","PhygitalConsentProvider","props","consentServiceProps","ConsentServiceProvider","usePhygitalConsent","context","useIsAnalyticsAllowed","usePhygitalConsent","isAnalyticsAllowed","import_react","import_jsx_runtime","POLICY_PLACEHOLDER","REFUSAL_MESSAGE","PolicyPopup","open","onClose","onReject","userId","onAgreeSuccess","agreed","setAgreed","showRefusalAlert","setShowRefusalAlert","createUserConsent","useCreateUserConsent","_data","variables","handleClose","handleRefuse","handleBackToPolicy","getConsentPayload","status","handleConfirmReject","handleAgree","e"]}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  'use client'
2
2
  var le=(n=>(n.IMAGE="image",n.VIDEO="video",n))(le||{}),ue=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(ue||{});import{createContext as Ue,useCallback as _e,useContext as Be,useEffect as Te,useState as Oe}from"react";import{useMemo as se,useState as L}from"react";var H="phygital_cookie_consent";function pe(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function fe(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:c="Lax",secure:d=!1}=n,l=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${c}`;d&&(l+="; Secure"),document.cookie=l}function B(){let e=pe(H);if(!e)return null;try{let t=JSON.parse(e);if(t&&typeof t=="object"&&"deviceId"in t&&"selected_preferences"in t&&typeof t.deviceId=="string"&&Array.isArray(t.selected_preferences))return t}catch{}return null}function K(e,t,n){fe(H,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}import{useQuery as T,useMutation as Y,useQueryClient as J}from"@tanstack/react-query";var E="",v=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${E}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${E}/consent/${t}`);return n},getPolicies:async t=>{let{data:n}=await e.get(`${E}/policy`,{params:t??void 0});return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${E}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${E}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${E}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${E}/cookies/device-id`,t);return n}});function me(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("").toLowerCase()}async function ge(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return me(n)}function Ce(e){let t=he(e),n=ye(),o=be(),s=new Uint32Array(64),c=t.length>>6;for(let l=0;l<c;l++){let u=l<<6;for(let p=0;p<16;p++){let b=u+(p<<2);s[p]=t[b]<<24|t[b+1]<<16|t[b+2]<<8|t[b+3]}for(let p=16;p<64;p++){let b=P(s[p-15],7)^P(s[p-15],18)^s[p-15]>>>3,_=P(s[p-2],17)^P(s[p-2],19)^s[p-2]>>>10;s[p]=s[p-16]+b+s[p-7]+_>>>0}let i=o[0],R=o[1],y=o[2],A=o[3],g=o[4],x=o[5],w=o[6],f=o[7];for(let p=0;p<64;p++){let b=P(g,6)^P(g,11)^P(g,25),_=g&x^~g&w,O=f+b+_+n[p]+s[p]>>>0,z=P(i,2)^P(i,13)^P(i,22),W=i&R^i&y^R&y,r=z+W>>>0;f=w,w=x,x=g,g=A+O>>>0,A=y,y=R,R=i,i=O+r>>>0}o[0]=o[0]+i>>>0,o[1]=o[1]+R>>>0,o[2]=o[2]+y>>>0,o[3]=o[3]+A>>>0,o[4]=o[4]+g>>>0,o[5]=o[5]+x>>>0,o[6]=o[6]+w>>>0,o[7]=o[7]+f>>>0}let d="";for(let l=0;l<8;l++){let u=o[l];d+=(u>>>28).toString(16)+(u>>>24&15).toString(16)+(u>>>20&15).toString(16)+(u>>>16&15).toString(16)+(u>>>12&15).toString(16)+(u>>>8&15).toString(16)+(u>>>4&15).toString(16)+(u&15).toString(16)}return d.toLowerCase()}function P(e,t){return e>>>t|e<<32-t}function he(e){let t=e.length,n=[];for(let u=0;u<t;u++){let i=e.charCodeAt(u);i<128?n.push(i):i<2048?n.push(192|i>>6,128|i&63):i<55296||i>=57344?n.push(224|i>>12,128|i>>6&63,128|i&63):(i=65536+((i&1023)<<10|e.charCodeAt(++u)&1023),n.push(240|i>>18,128|i>>12&63,128|i>>6&63,128|i&63))}let o=n.length,s=(64-(o+9)%64)%64,c=o+9+s,d=new Uint8Array(c);d.set(n),d[o]=128;let l=new DataView(d.buffer,d.byteOffset,d.byteLength);return l.setUint32(c-8,0,!1),l.setUint32(c-4,o*8>>>0,!1),d}function ye(){let e=[];return"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2".split(" ").forEach(n=>e.push(parseInt(n,16))),e}function be(){return[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]}function xe(){return!(typeof crypto>"u"||!crypto.subtle)}async function U(e){return typeof e!="string"||e.length===0?e:xe()?ge(e):Promise.resolve(Ce(e))}import{QueryClient as ve,QueryClientProvider as Pe}from"@tanstack/react-query";import ke from"axios";import{createContext as Re,useContext as we,useMemo as F}from"react";import{jsx as V}from"react/jsx-runtime";var $=Re(void 0),Se=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=ke.create({baseURL:e,...t});return s.interceptors.request.use(n.onFulfilled,n.onRejected),s.interceptors.response.use(o.onFulfilled,o.onRejected),{consentApi:s,updateHeaders:d=>{Object.entries(d).forEach(([l,u])=>{s.defaults.headers.common[l]=u})}}},j=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:c={},responseInterceptors:d={}})=>{let l=F(()=>o||new ve({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),u=F(()=>{let i=Se({baseURL:t,axiosConfig:n,requestInterceptors:c,responseInterceptors:d});return{consentApi:i.consentApi,updateHeaders:i.updateHeaders,queryClient:l}},[t,l,n,c,d]);return V($.Provider,{value:u,children:V(Pe,{client:l,children:e})})},k=()=>{let e=we($);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};var C={all:["consent"],health:()=>[...C.all,"health"],list:e=>[...C.all,"list",e],detail:e=>[...C.all,"detail",e],latestByUserId:e=>[...C.all,"user-id",e],latestCookieByDeviceId:e=>[...C.all,"cookies","device-id",e]},ft=e=>{let{consentApi:t}=k(),n=v(t);return T({queryKey:C.health(),queryFn:()=>n.getHealth(),...e})},mt=(e,t)=>{let{consentApi:n}=k(),o=v(n);return T({queryKey:C.list(e),queryFn:()=>o.getManyConsents(e),...t})},gt=(e,t)=>{let{consentApi:n}=k(),o=v(n);return T({queryKey:C.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},Ct=(e,t)=>{let{consentApi:n}=k(),o=v(n);return T({queryKey:C.latestByUserId(e.user_id),queryFn:async()=>{let s=await U(e.user_id);return o.getLatestConsentByUserId({user_id:s})},enabled:!!e.user_id,...t})},ht=(e,t)=>{let{consentApi:n}=k(),o=v(n);return T({queryKey:C.latestCookieByDeviceId(e.device_id),queryFn:async()=>{let s=await U(e.device_id);return o.getLatestCookieConsentByDeviceId({device_id:s})},enabled:!!e.device_id,...t})},X=e=>{let{consentApi:t}=k(),n=J(),o=v(t);return Y({mutationFn:async s=>{let c={...s};return s.user_id!=null&&s.user_id!==""&&(c.user_id=await U(s.user_id)),s.device_id!=null&&s.device_id!==""&&(c.device_id=await U(s.device_id)),o.createUserConsent(c)},onSuccess:(s,c)=>{c.user_id&&n.invalidateQueries({queryKey:C.latestByUserId(c.user_id)}),n.invalidateQueries({queryKey:C.list()})},...e})},Z=e=>{let{consentApi:t}=k(),n=J(),o=v(t);return Y({mutationFn:async s=>{let c={...s,device_id:await U(s.device_id)};return o.createDeviceCookieConsent(c)},onSuccess:(s,c)=>{n.invalidateQueries({queryKey:C.latestCookieByDeviceId(c.device_id)}),n.invalidateQueries({queryKey:C.list()})},...e})};import{useQuery as Ie}from"@tanstack/react-query";var ee={all:["policy"],list:e=>[...ee.all,"list",e]},te=(e,t)=>{let{consentApi:n}=k(),o=v(n);return Ie({queryKey:ee.list(e),queryFn:()=>o.getPolicies(e),...t})};import{Fragment as Ae,jsx as a,jsxs as S}from"react/jsx-runtime";function re({onSubmitted:e,onDismiss:t,className:n=""}){let[o,s]=L(!1),[c,d]=L(!1),[l,u]=L(null),[i,R]=L({}),{data:y}=te({type:"COOKIE_PREFERENCE",status:"ACTIVE"}),g=se(()=>y?.data?y?.data?.find(r=>r.type==="COOKIE_PREFERENCE"&&r.status==="ACTIVE")??y?.data?.[0]:null,[y])?.preferences??[],x=se(()=>({...Object.fromEntries(g.map(m=>[m.id??"",!0]).filter(([m])=>!!m)),...i}),[g,i]),{deviceId:w}=q(),f=Z({onSuccess:(r,m)=>{K(w,m.selected_preferences??[]),e?.(),t?.(),s(!0),d(!1)}}),p=()=>g.map(r=>r.id).filter(r=>!!r).filter(r=>x[r]),b=(r,m)=>({device_id:w,status:r,selected_preferences:m,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),_=r=>{g.find(I=>I.id===r)?.is_mandatory||R(I=>({...I,[r]:!x[r]}))},O=()=>{f.mutate(b("REJECTED",[]))},z=()=>{let r=g.map(m=>m.id).filter(m=>!!m);f.mutate(b("ACCEPTED",r.length>0?r:[]))},W=()=>{let r=p();f.mutate(b("ACCEPTED",r.length>0?r:["essential"]))};return o?null:S(Ae,{children:[S("div",{role:"dialog","aria-label":"Th\xF4ng b\xE1o v\u1EC1 cookie",className:`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${n}`,style:{position:"fixed",bottom:0,left:0,right:0,zIndex:50,display:"flex",flexDirection:"column",gap:"1rem",borderRadius:"0.75rem 0.75rem 0 0",borderWidth:"1px 1px 0 1px",borderColor:"#e5e7eb",backgroundColor:"#fff",padding:"1rem",boxShadow:"0 -4px 6px -1px rgb(0 0 0 / 0.1)"},children:[a("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:"Th\xF4ng b\xE1o v\u1EC1 cookie"}),S("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",display:"flex",flexDirection:"column",gap:"0.5rem"},children:[a("p",{children:"TapQuest s\u1EED d\u1EE5ng tr\xECnh theo d\xF5i (nh\u01B0 Cookie ho\u1EB7c SDK) cho:"}),a("ul",{style:{margin:0,paddingLeft:"1.25rem"},children:g.map(r=>a("li",{children:r.name??r.id??""},r.id??r.name))}),a("p",{children:"Tr\u01B0\u1EDBc khi b\u1EA1n \u0111\u1ED3ng \xFD, ch\u1EC9 nh\u1EEFng tr\xECnh theo d\xF5i ho\xE0n to\xE0n c\u1EA7n thi\u1EBFt m\u1EDBi \u0111\u01B0\u1EE3c tri\u1EC3n khai."}),a("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn Ch\u1EA5p nh\u1EADn t\u1EA5t c\u1EA3, b\u1EA1n \u0111\u1ED3ng \xFD s\u1EED d\u1EE5ng t\u1EA5t c\u1EA3 tr\xECnh theo d\xF5i c\u1EE7a ch\xFAng t\xF4i."}),a("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn T\u1EEB ch\u1ED1i t\u1EA5t c\u1EA3, b\u1EA1n t\u1EEB ch\u1ED1i nh\u1EEFng tr\xECnh theo d\xF5i y\xEAu c\u1EA7u s\u1EF1 \u0111\u1ED3ng \xFD c\u1EE7a b\u1EA1n v\xE0 s\u1EBD kh\xF4ng c\xF3 quy\u1EC1n truy c\u1EADp v\xE0o c\xE1c \u0111\u1EC1 ngh\u1ECB ho\u1EB7c n\u1ED9i dung \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a."}),a("p",{children:"N\u1EBFu b\u1EA1n ch\u1ECDn T\xF9y ch\u1ECDn, b\u1EA1n c\xF3 th\u1EC3 ch\u1ECDn xem b\u1EA1n c\xF3 \u0111\u1ED3ng \xFD s\u1EED d\u1EE5ng tr\xECnh theo d\xF5i tr\xEAn \u1EE9ng d\u1EE5ng c\u1EE7a ch\xFAng t\xF4i hay kh\xF4ng v\xE0 \u1EDF m\u1EE9c \u0111\u1ED9 n\xE0o."}),S("p",{children:["B\u1EA1n c\xF3 th\u1EC3 r\xFAt l\u1EA1i s\u1EF1 \u0111\u1ED3ng \xFD c\u1EE7a m\xECnh b\u1EA5t k\u1EF3 l\xFAc n\xE0o b\u1EB1ng c\xE1ch nh\u1EA5p v\xE0o li\xEAn k\u1EBFt trong"," ","Ch\xEDnh s\xE1ch cookie"," ","c\u1EE7a ch\xFAng t\xF4i."]})]}),S("div",{className:"flex justify-end gap-2 flex-wrap",children:[a("button",{type:"button",onClick:()=>d(!0),className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\xF9y ch\u1ECDn"}),a("button",{type:"button",onClick:O,disabled:f.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i t\u1EA5t c\u1EA3"}),a("button",{type:"button",onClick:z,disabled:f.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"Ch\u1EA5p nh\u1EADn t\u1EA5t c\u1EA3"})]}),f.isError&&a("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]}),c&&a("div",{role:"dialog","aria-modal":"true","aria-label":"Trung t\xE2m tu\u1EF3 ch\u1ECDn b\u1EA3o m\u1EADt",style:{position:"fixed",inset:0,zIndex:60,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:r=>r.target===r.currentTarget&&d(!1),children:S("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"28rem",width:"100%",maxHeight:"90vh",overflowY:"auto",padding:"1.25rem",position:"relative"},onClick:r=>r.stopPropagation(),children:[a("button",{type:"button",onClick:()=>d(!1),"aria-label":"\u0110\xF3ng",style:{position:"absolute",top:"1rem",right:"1rem",width:"2rem",height:"2rem",borderRadius:"0.25rem",border:"none",background:"transparent",cursor:"pointer",fontSize:"1.25rem",lineHeight:1,color:"#6b7280"},children:"\xD7"}),a("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827",marginBottom:"0.75rem",paddingRight:"2rem"},children:"Trung t\xE2m tu\u1EF3 ch\u1ECDn b\u1EA3o m\u1EADt"}),S("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",marginBottom:"1rem"},children:[a("p",{style:{marginBottom:"0.5rem"},children:"Khi b\u1EA1n truy c\u1EADp b\u1EA5t k\u1EF3 trang web n\xE0o, trang web \u0111\xF3 c\xF3 th\u1EC3 l\u01B0u tr\u1EEF ho\u1EB7c truy xu\u1EA5t th\xF4ng tin tr\xEAn tr\xECnh duy\u1EC7t c\u1EE7a b\u1EA1n, ch\u1EE7 y\u1EBFu d\u01B0\u1EDBi d\u1EA1ng cookie. Th\xF4ng tin n\xE0y c\xF3 th\u1EC3 li\xEAn quan \u0111\u1EBFn b\u1EA1n, t\xF9y ch\u1ECDn c\u1EE7a b\u1EA1n ho\u1EB7c thi\u1EBFt b\u1ECB c\u1EE7a b\u1EA1n, v\xE0 ch\u1EE7 y\u1EBFu \u0111\u01B0\u1EE3c s\u1EED d\u1EE5ng \u0111\u1EC3 trang web ho\u1EA1t \u0111\u1ED9ng nh\u01B0 mong \u0111\u1EE3i."}),a("p",{style:{marginBottom:"0.5rem"},children:"Th\xF4ng tin n\xE0y th\u01B0\u1EDDng kh\xF4ng tr\u1EF1c ti\u1EBFp x\xE1c \u0111\u1ECBnh b\u1EA1n nh\u01B0ng c\xF3 th\u1EC3 mang l\u1EA1i tr\u1EA3i nghi\u1EC7m web \u0111\u01B0\u1EE3c c\xE1 nh\xE2n h\xF3a h\u01A1n. Ch\xFAng t\xF4i t\xF4n tr\u1ECDng quy\u1EC1n ri\xEAng t\u01B0 c\u1EE7a b\u1EA1n; b\u1EA1n c\xF3 th\u1EC3 ch\u1ECDn kh\xF4ng cho ph\xE9p m\u1ED9t s\u1ED1 lo\u1EA1i cookie. Nh\u1EA5p v\xE0o ti\xEAu \u0111\u1EC1 t\u1EEBng danh m\u1EE5c \u0111\u1EC3 t\xECm hi\u1EC3u th\xEAm v\xE0 thay \u0111\u1ED5i c\xE0i \u0111\u1EB7t m\u1EB7c \u0111\u1ECBnh. Vi\u1EC7c ch\u1EB7n m\u1ED9t s\u1ED1 cookie c\xF3 th\u1EC3 \u1EA3nh h\u01B0\u1EDFng \u0111\u1EBFn tr\u1EA3i nghi\u1EC7m c\u1EE7a b\u1EA1n v\u1EDBi trang web v\xE0 c\xE1c d\u1ECBch v\u1EE5 \u0111\u01B0\u1EE3c cung c\u1EA5p."}),a("p",{style:{marginBottom:"0.5rem"},children:'N\u1EBFu ch\u1ECDn "Kh\xF4ng theo d\xF5i" khi l\u1EA7n \u0111\u1EA7u s\u1EED d\u1EE5ng TapQuest, ch\u1EC9 cookie ho\xE0n to\xE0n c\u1EA7n thi\u1EBFt m\u1EDBi \u0111\u01B0\u1EE3c s\u1EED d\u1EE5ng.'}),a("span",{style:{color:"#2563eb",textDecoration:"underline"},children:"Th\xF4ng tin kh\xE1c"})]}),a("h3",{className:"consent:text-sm consent:font-semibold consent:text-gray-900",style:{fontSize:"0.875rem",fontWeight:600,color:"#111827",marginBottom:"0.75rem"},children:"Qu\u1EA3n l\xFD tu\u1EF3 ch\u1ECDn \u0111\u1ED3ng \xFD"}),a("div",{style:{display:"flex",flexDirection:"column",gap:"0.25rem",marginBottom:"1.25rem"},children:g.map(r=>{let m=r.id??r.name??"",I=x[m],N=l===m,G=r.is_mandatory===!0;return S("div",{style:{border:"1px solid #e5e7eb",borderRadius:"0.5rem",overflow:"hidden"},children:[S("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.75rem",cursor:"pointer",backgroundColor:N?"#f9fafb":"transparent"},onClick:()=>u(N?null:m),children:[a("span",{style:{flexShrink:0,width:"1.25rem",height:"1.25rem",display:"flex",alignItems:"center",justifyContent:"center",fontSize:"1rem",color:"#6b7280",transform:N?"rotate(45deg)":"none",transition:"transform 0.2s"},children:"+"}),a("span",{className:"consent:font-medium consent:text-gray-900",style:{flex:1,fontSize:"0.875rem",fontWeight:500,color:"#111827"},children:r.name??m}),a("button",{type:"button",role:"switch","aria-checked":I,"aria-label":`${r.name??m} ${I?"b\u1EADt":"t\u1EAFt"}`,disabled:G,onClick:de=>{de.stopPropagation(),_(m)},style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:G?"default":"pointer",backgroundColor:G?"#e5e7eb":I?"#111827":"#d1d5db",opacity:G?.7:1,transition:"background-color 0.2s"},children:a("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:I?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]}),N&&a("div",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563",padding:"0.75rem 0.75rem 0.75rem 2.5rem",borderTop:"1px solid #e5e7eb"},children:r.description??""})]},m)})}),a("button",{type:"button",onClick:W,disabled:f.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer",width:"100%"},children:"X\xE1c nh\u1EADn l\u1EF1a ch\u1ECDn c\u1EE7a t\xF4i"})]})})]})}var Ee="analytics",De="advertising";function Q(){let e=B();return e?.selected_preferences?e.selected_preferences.includes(Ee):!1}function ie(){let e=B();return e?.selected_preferences?e.selected_preferences.includes(De):!1}import{jsx as M,jsxs as Ge}from"react/jsx-runtime";var ce=Ue(void 0);function Ne({children:e,deviceId:t,showBannerWhenNoPreference:n=!0}){let[o,s]=Oe(!1),c=_e(()=>{let l=B();s(!!l)},[]);Te(()=>{c()},[c]);let d={deviceId:t,hasConsentPreference:o,isAnalyticsAllowed:Q(),isAdvertisingAllowed:ie(),refreshConsentPreference:c};return Ge(ce.Provider,{value:d,children:[e,n&&!o&&M(re,{onSubmitted:c})]})}function zt(e){let{children:t,deviceId:n,showBannerWhenNoPreference:o=!0,...s}=e;return M(j,{...s,children:M(Ne,{deviceId:n,showBannerWhenNoPreference:o,children:t})})}function q(){let e=Be(ce);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}function Yt(){return q(),Q()}import{useState as ae}from"react";import{Fragment as Qe,jsx as h,jsxs as D}from"react/jsx-runtime";var Le=`
3
3
  Tanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.
4
- `.trim(),qe="V\xEC b\u1EA1n kh\xF4ng \u0111\u1ED3ng \xFD v\u1EDBi th\u1ECFa thu\u1EADn n\xE0y. Ch\xFAng t\xF4i s\u1EBD x\xF3a t\xE0i kho\u1EA3n trong v\xF2ng x ng\xE0y. Nh\u1EA5n n\xFAt '\u0110\u1ED3ng \xFD' \u0111\u1EC3 ti\u1EBFp t\u1EE5c";function dn({open:e,onClose:t,onReject:n,userId:o,onAgreeSuccess:s}){let[c,d]=ae(!1),[l,u]=ae(!1),i=X({onSuccess:(f,p)=>{p.status==="ACCEPTED"&&s?.(),R()}}),R=()=>{d(!1),u(!1),t()},y=()=>{u(!0)},A=()=>{u(!1)},g=f=>({user_id:o,status:f,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),x=()=>{i.mutate(g("REJECTED")),n?.()},w=()=>{c&&i.mutate(g("ACCEPTED"))};return e?D(Qe,{children:[h("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",padding:"1rem"},children:D("div",{className:"consent:overflow-hidden consent:flex consent:flex-col",style:{width:"100%",height:"100%",overflow:"hidden",display:"flex",flexDirection:"column"},onClick:f=>f.stopPropagation(),children:[h("h2",{style:{fontSize:"18px",fontWeight:600,color:"#000",padding:"20px 2.5rem 12px 20px"},children:"Ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt"}),h("div",{style:{flex:1,overflowY:"auto",padding:"1rem 1.25rem",fontSize:"0.875rem",color:"#111827",lineHeight:1.6},children:h("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:Le})}),D("div",{style:{padding:"1rem 1.25rem 1.25rem 1.25rem",display:"flex",flexDirection:"column",gap:"1rem"},children:[D("label",{style:{display:"flex",alignItems:"center",gap:"0.5rem",fontSize:"0.875rem",color:"#111827",cursor:"pointer"},children:[h("input",{type:"checkbox",checked:c,onChange:f=>d(f.target.checked),style:{width:"1rem",height:"1rem",accentColor:"#111827"},"aria-label":"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"}),h("span",{children:"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"})]}),D("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[h("button",{type:"button",onClick:y,disabled:i.isPending,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i"}),h("button",{type:"button",onClick:w,disabled:!c||i.isPending,className:"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:c?"#111827":"#e5e7eb",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:c?"#fff":"#9ca3af",border:"none",cursor:c?"pointer":"not-allowed"},children:"Ti\u1EBFp t\u1EE5c"})]}),i.isError&&h("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:0},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})]})}),l&&h("div",{role:"alertdialog","aria-modal":"true","aria-label":"Th\xF4ng b\xE1o t\u1EEB ch\u1ED1i",style:{position:"fixed",inset:0,zIndex:70,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:f=>f.target===f.currentTarget&&A(),children:D("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"22rem",width:"100%",padding:"1.25rem"},onClick:f=>f.stopPropagation(),children:[h("p",{className:"consent:text-sm consent:text-gray-700",style:{fontSize:"0.875rem",color:"#374151",margin:"0 0 1rem 0",lineHeight:1.5},children:qe}),D("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[h("button",{type:"button",onClick:A,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"Tr\u1EDF l\u1EA1i"}),h("button",{type:"button",onClick:x,disabled:i.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:"#a71f24",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"\u0110\u1ED3ng \xFD"})]}),i.isError&&h("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:"0.5rem 0 0 0"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})})]}):null}export{H as COOKIE_CONSENT_NAME,re as CookieConsentBanner,ue as EntityStatus,le as MediaType,zt as PhygitalConsentProvider,dn as PolicyPopup,C as consentQueryKeys,v as consentService,B as getConsentPreferenceCookie,pe as getCookie,ie as isAdvertisingAllowed,Q as isAnalyticsAllowed,ee as policyQueryKeys,K as setConsentPreferenceCookie,fe as setCookie,U as sha256,gt as useConsentById,Z as useCreateDeviceCookieConsent,X as useCreateUserConsent,ft as useHealth,Yt as useIsAnalyticsAllowed,Ct as useLatestConsentByUserId,ht as useLatestCookieConsentByDeviceId,mt as useManyConsents,q as usePhygitalConsent,te as usePolicies};
4
+ `.trim(),qe="V\xEC b\u1EA1n kh\xF4ng \u0111\u1ED3ng \xFD v\u1EDBi th\u1ECFa thu\u1EADn n\xE0y. Ch\xFAng t\xF4i s\u1EBD x\xF3a t\xE0i kho\u1EA3n trong v\xF2ng x ng\xE0y. Nh\u1EA5n n\xFAt '\u0110\u1ED3ng \xFD' \u0111\u1EC3 ti\u1EBFp t\u1EE5c";function dn({open:e,onClose:t,onReject:n,userId:o,onAgreeSuccess:s}){let[c,d]=ae(!1),[l,u]=ae(!1),i=X({onSuccess:(f,p)=>{p.status==="ACCEPTED"&&s?.(),R()}}),R=()=>{d(!1),u(!1),t()},y=()=>{u(!0)},A=()=>{u(!1)},g=f=>({user_id:o,status:f,page_url:typeof window<"u"?window.location.href:void 0,user_agent:typeof navigator<"u"?navigator.userAgent:void 0}),x=()=>{i.mutate(g("REJECTED")),n?.()},w=()=>{c&&i.mutate(g("ACCEPTED"))};return e?D(Qe,{children:[h("div",{style:{display:"flex",alignItems:"center",justifyContent:"center"},children:D("div",{className:"consent:overflow-hidden consent:flex consent:flex-col",style:{width:"100%",height:"100%",overflow:"hidden",display:"flex",flexDirection:"column"},onClick:f=>f.stopPropagation(),children:[h("h2",{style:{fontSize:"20px",fontWeight:600,color:"#000",padding:"20px 2.5rem 12px 20px"},children:"Ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt"}),h("div",{style:{flex:1,overflowY:"auto",padding:"1rem 1.25rem",fontSize:"0.875rem",color:"#111827",lineHeight:1.6},children:h("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:Le})}),D("div",{style:{padding:"1rem 1.25rem 1.25rem 1.25rem",display:"flex",flexDirection:"column",gap:"1rem"},children:[D("label",{style:{display:"flex",alignItems:"center",gap:"0.5rem",fontSize:"0.875rem",color:"#111827",cursor:"pointer"},children:[h("input",{type:"checkbox",checked:c,onChange:f=>d(f.target.checked),style:{width:"1rem",height:"1rem",accentColor:"#111827"},"aria-label":"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"}),h("span",{children:"T\xF4i \u0111\u1ED3ng \xFD v\u1EDBi ch\xEDnh s\xE1ch b\u1EA3o m\u1EADt tr\xEAn"})]}),D("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[h("button",{type:"button",onClick:y,disabled:i.isPending,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"T\u1EEB ch\u1ED1i"}),h("button",{type:"button",onClick:w,disabled:!c||i.isPending,className:"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:c?"#111827":"#e5e7eb",padding:"0.5rem 1rem",fontSize:"1rem",fontWeight:500,color:c?"#fff":"#9ca3af",border:"none",cursor:c?"pointer":"not-allowed"},children:"Ti\u1EBFp t\u1EE5c"})]}),i.isError&&h("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"1rem",color:"#dc2626",margin:0},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})]})}),l&&h("div",{role:"alertdialog","aria-modal":"true","aria-label":"Th\xF4ng b\xE1o t\u1EEB ch\u1ED1i",style:{position:"fixed",inset:0,zIndex:70,display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0,0,0,0.5)",padding:"1rem"},onClick:f=>f.target===f.currentTarget&&A(),children:D("div",{className:"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg",style:{backgroundColor:"#fff",borderRadius:"0.75rem",border:"1px solid #e5e7eb",boxShadow:"0 10px 15px -3px rgb(0 0 0 / 0.1)",maxWidth:"22rem",width:"100%",padding:"1.25rem"},onClick:f=>f.stopPropagation(),children:[h("p",{className:"consent:text-sm consent:text-gray-700",style:{fontSize:"1rem",color:"#374151",margin:"0 0 1rem 0",lineHeight:1.5},children:qe}),D("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[h("button",{type:"button",onClick:A,className:"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50",style:{flex:1,borderRadius:"0.5rem",border:"1px solid #111827",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#111827",cursor:"pointer"},children:"Tr\u1EDF l\u1EA1i"}),h("button",{type:"button",onClick:x,disabled:i.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1",style:{flex:1,minWidth:0,borderRadius:"0.5rem",background:"#a71f24",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff",border:"none",cursor:"pointer"},children:"\u0110\u1ED3ng \xFD"})]}),i.isError&&h("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626",margin:"0.5rem 0 0 0"},children:"\u0110\xE3 x\u1EA3y ra l\u1ED7i. Vui l\xF2ng th\u1EED l\u1EA1i."})]})})]}):null}export{H as COOKIE_CONSENT_NAME,re as CookieConsentBanner,ue as EntityStatus,le as MediaType,zt as PhygitalConsentProvider,dn as PolicyPopup,C as consentQueryKeys,v as consentService,B as getConsentPreferenceCookie,pe as getCookie,ie as isAdvertisingAllowed,Q as isAnalyticsAllowed,ee as policyQueryKeys,K as setConsentPreferenceCookie,fe as setCookie,U as sha256,gt as useConsentById,Z as useCreateDeviceCookieConsent,X as useCreateUserConsent,ft as useHealth,Yt as useIsAnalyticsAllowed,Ct as useLatestConsentByUserId,ht as useLatestCookieConsentByDeviceId,mt as useManyConsents,q as usePhygitalConsent,te as usePolicies};
5
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/components/CookieConsentBanner.tsx","../src/helpers/cookie.ts","../src/hooks/useConsent.ts","../src/api/consent.ts","../src/helpers/sha256.ts","../src/provider/ConsentServiceProvider.tsx","../src/hooks/usePolicy.ts","../src/helpers/analytics-consent.ts","../src/hooks/useAnalyticsConsent.ts","../src/components/PolicyPopup.tsx"],"sourcesContent":["export enum MediaType {\n IMAGE = \"image\",\n VIDEO = \"video\",\n}\n\nexport type DateTimeNumber = number;\n\nexport interface Media {\n url: string;\n type: MediaType | string;\n thumbnail_url?: string;\n}\n\nexport enum EntityStatus {\n ACTIVE = \"Active\",\n INACTIVE = \"Inactive\",\n}\n\nexport interface CommonModel {\n id: string;\n status: EntityStatus;\n created_at: DateTimeNumber;\n updated_at: DateTimeNumber;\n created_by?: string;\n updated_by?: string;\n}\n","\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useState } from \"react\";\nimport { CookieConsentBanner } from \"../components/CookieConsentBanner\";\nimport { isAdvertisingAllowed, isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n deviceId: string;\n hasConsentPreference: boolean;\n isAnalyticsAllowed: boolean;\n isAdvertisingAllowed: boolean;\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n deviceId: string;\n children: React.ReactNode;\n showBannerWhenNoPreference?: boolean;\n}\n\n/** Inner provider: runs inside ConsentServiceProvider so useLatestConsentByUserId has context. */\nfunction PhygitalConsentProviderInner({\n children,\n deviceId,\n showBannerWhenNoPreference = true,\n}: Pick<PhygitalConsentProviderProps, \"children\" | \"deviceId\" | \"showBannerWhenNoPreference\">) {\n const [hasConsentPreference, setHasConsentPreference] = useState(false);\n\n const refreshConsentPreference = useCallback(() => {\n const stored = getConsentPreferenceCookie();\n setHasConsentPreference(!!stored);\n }, []);\n\n useEffect(() => {\n refreshConsentPreference();\n }, [refreshConsentPreference]);\n\n const value: PhygitalConsentContextValue = {\n deviceId,\n hasConsentPreference,\n isAnalyticsAllowed: isAnalyticsAllowed(),\n isAdvertisingAllowed: isAdvertisingAllowed(),\n refreshConsentPreference,\n };\n\n return (\n <PhygitalConsentContext.Provider value={value}>\n {children}\n {showBannerWhenNoPreference && !hasConsentPreference && (\n <CookieConsentBanner onSubmitted={refreshConsentPreference} />\n )}\n </PhygitalConsentContext.Provider>\n );\n}\n\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, deviceId, showBannerWhenNoPreference = true, ...consentServiceProps } = props;\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentProviderInner\n deviceId={deviceId}\n showBannerWhenNoPreference={showBannerWhenNoPreference}\n >\n {children}\n </PhygitalConsentProviderInner>\n </ConsentServiceProvider>\n );\n}\n\nexport function usePhygitalConsent() {\n const context = useContext(PhygitalConsentContext);\n if (!context) {\n throw new Error(\"usePhygitalConsent must be used within a PhygitalConsentProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { COOKIE_POLICY_URL, OTHER_INFO_URL } from \"../env/constant\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\nimport { usePolicies } from \"../hooks/usePolicy\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\nexport interface CookieConsentBannerProps {\n /** Called after consent is submitted successfully (Reject or Accept). */\n onSubmitted?: () => void;\n /** Called when the banner is dismissed (e.g. hide from UI). */\n onDismiss?: () => void;\n /** Optional class name for the root container. */\n className?: string;\n}\n\nexport function CookieConsentBanner({\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [showCustomModal, setShowCustomModal] = useState(false);\n const [expandedKey, setExpandedKey] = useState<string | null>(null);\n const [preferences, setPreferences] = useState<Record<string, boolean>>({});\n\n const { data: policyResponse } = usePolicies({ type: \"COOKIE_PREFERENCE\", status: \"ACTIVE\" });\n\n const policy = useMemo(\n () => {\n if (!policyResponse?.data) return null;\n\n return policyResponse?.data?.find((p) => p.type === \"COOKIE_PREFERENCE\" && p.status === \"ACTIVE\") ?? policyResponse?.data?.[0];\n },\n [policyResponse]\n );\n const preferenceList = policy?.preferences ?? [];\n\n const effectivePreferences = useMemo(() => {\n const defaultAllTrue = Object.fromEntries(\n preferenceList.map((p) => [p.id ?? \"\", true]).filter(([id]) => !!id)\n );\n return { ...defaultAllTrue, ...preferences };\n }, [preferenceList, preferences]);\n\n const { deviceId } = usePhygitalConsent();\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n setShowCustomModal(false);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n preferenceList\n .map((p) => p.id)\n .filter((id): id is string => !!id)\n .filter((id) => effectivePreferences[id]);\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\", selected_preferences: string[]) => ({\n device_id: deviceId,\n status,\n selected_preferences,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const togglePreference = (id: string) => {\n const pref = preferenceList.find((p) => p.id === id);\n if (pref?.is_mandatory) return;\n setPreferences((prev) => ({ ...prev, [id]: !effectivePreferences[id] }));\n };\n\n const handleReject = () => {\n createConsent.mutate(getConsentPayload(\"REJECTED\", []));\n };\n\n const handleAcceptAll = () => {\n const ids = preferenceList.map((p) => p.id).filter((id): id is string => !!id);\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", ids.length > 0 ? ids : []));\n };\n\n const handleSaveCustom = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", selected.length > 0 ? selected : [\"essential\"]));\n };\n\n if (dismissed) return null;\n\n return (\n <>\n {/* Screen 1: Bottom banner */}\n <div\n role=\"dialog\"\n aria-label=\"Thông báo về cookie\"\n className={`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${className}`}\n style={{\n position: \"fixed\",\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 50,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n borderRadius: \"0.75rem 0.75rem 0 0\",\n borderWidth: \"1px 1px 0 1px\",\n borderColor: \"#e5e7eb\",\n backgroundColor: \"#fff\",\n padding: \"1rem\",\n boxShadow: \"0 -4px 6px -1px rgb(0 0 0 / 0.1)\",\n }}\n >\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\" }}\n >\n Thông báo về cookie\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\" }}\n >\n <p>\n TapQuest sử dụng trình theo dõi (như Cookie hoặc SDK) cho:\n </p>\n <ul style={{ margin: 0, paddingLeft: \"1.25rem\" }}>\n {preferenceList.map((p) => (\n <li key={p.id ?? p.name}>{p.name ?? p.id ?? \"\"}</li>\n ))}\n </ul>\n <p>\n Trước khi bạn đồng ý, chỉ những trình theo dõi hoàn toàn cần thiết mới được triển khai.\n </p>\n <p>\n Nếu bạn chọn Chấp nhận tất cả, bạn đồng ý sử dụng tất cả trình theo dõi của chúng tôi.\n </p>\n <p>\n Nếu bạn chọn Từ chối tất cả, bạn từ chối những trình theo dõi yêu cầu sự đồng ý của bạn và sẽ không có quyền truy cập vào các đề nghị hoặc nội dung được cá nhân hóa.\n </p>\n <p>\n Nếu bạn chọn Tùy chọn, bạn có thể chọn xem bạn có đồng ý sử dụng trình theo dõi trên ứng dụng của chúng tôi hay không và ở mức độ nào.\n </p>\n <p>\n Bạn có thể rút lại sự đồng ý của mình bất kỳ lúc nào bằng cách nhấp vào liên kết trong{\" \"}\n {COOKIE_POLICY_URL ? (\n <a\n href={COOKIE_POLICY_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Chính sách cookie\n </a>\n ) : (\n \"Chính sách cookie\"\n )}{\" \"}\n của chúng tôi.\n </p>\n </div>\n <div className=\"flex justify-end gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(true)}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Tùy chọn\n </button>\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Từ chối tất cả\n </button>\n <button\n type=\"button\"\n onClick={handleAcceptAll}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Chấp nhận tất cả\n </button>\n </div>\n {createConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n\n {/* Screen 2: Custom preferences modal */}\n {showCustomModal && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Trung tâm tuỳ chọn bảo mật\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 60,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && setShowCustomModal(false)}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"28rem\",\n width: \"100%\",\n maxHeight: \"90vh\",\n overflowY: \"auto\",\n padding: \"1.25rem\",\n position: \"relative\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(false)}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button>\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\", paddingRight: \"2rem\" }}\n >\n Trung tâm tuỳ chọn bảo mật\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", marginBottom: \"1rem\" }}\n >\n <p style={{ marginBottom: \"0.5rem\" }}>\n Khi bạn truy cập bất kỳ trang web nào, trang web đó có thể lưu trữ hoặc truy xuất thông tin trên trình duyệt của bạn, chủ yếu dưới dạng cookie. Thông tin này có thể liên quan đến bạn, tùy chọn của bạn hoặc thiết bị của bạn, và chủ yếu được sử dụng để trang web hoạt động như mong đợi.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Thông tin này thường không trực tiếp xác định bạn nhưng có thể mang lại trải nghiệm web được cá nhân hóa hơn. Chúng tôi tôn trọng quyền riêng tư của bạn; bạn có thể chọn không cho phép một số loại cookie. Nhấp vào tiêu đề từng danh mục để tìm hiểu thêm và thay đổi cài đặt mặc định. Việc chặn một số cookie có thể ảnh hưởng đến trải nghiệm của bạn với trang web và các dịch vụ được cung cấp.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Nếu chọn \"Không theo dõi\" khi lần đầu sử dụng TapQuest, chỉ cookie hoàn toàn cần thiết mới được sử dụng.\n </p>\n {OTHER_INFO_URL ? (\n <a\n href={OTHER_INFO_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Thông tin khác\n </a>\n ) : (\n <span style={{ color: \"#2563eb\", textDecoration: \"underline\" }}>Thông tin khác</span>\n )}\n </div>\n <h3\n className=\"consent:text-sm consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\" }}\n >\n Quản lý tuỳ chọn đồng ý\n </h3>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", marginBottom: \"1.25rem\" }}>\n {preferenceList.map((pref) => {\n const id = pref.id ?? pref.name ?? \"\";\n const checked = effectivePreferences[id];\n const isExpanded = expandedKey === id;\n const disabled = pref.is_mandatory === true;\n return (\n <div\n key={id}\n style={{\n border: \"1px solid #e5e7eb\",\n borderRadius: \"0.5rem\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n cursor: \"pointer\",\n backgroundColor: isExpanded ? \"#f9fafb\" : \"transparent\",\n }}\n onClick={() => setExpandedKey(isExpanded ? null : id)}\n >\n <span\n style={{\n flexShrink: 0,\n width: \"1.25rem\",\n height: \"1.25rem\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"1rem\",\n color: \"#6b7280\",\n transform: isExpanded ? \"rotate(45deg)\" : \"none\",\n transition: \"transform 0.2s\",\n }}\n >\n +\n </span>\n <span\n className=\"consent:font-medium consent:text-gray-900\"\n style={{ flex: 1, fontSize: \"0.875rem\", fontWeight: 500, color: \"#111827\" }}\n >\n {pref.name ?? id}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${pref.name ?? id} ${checked ? \"bật\" : \"tắt\"}`}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation();\n togglePreference(id);\n }}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: disabled ? \"default\" : \"pointer\",\n backgroundColor: disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: disabled ? 0.7 : 1,\n transition: \"background-color 0.2s\",\n }}\n >\n <span\n style={{\n display: \"block\",\n width: 20,\n height: 20,\n borderRadius: \"50%\",\n backgroundColor: \"#fff\",\n marginLeft: checked ? 22 : 2,\n marginTop: 2,\n transition: \"margin-left 0.2s\",\n boxShadow: \"0 1px 2px rgb(0 0 0 / 0.2)\",\n }}\n />\n </button>\n </div>\n {isExpanded && (\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#4b5563\",\n padding: \"0.75rem 0.75rem 0.75rem 2.5rem\",\n borderTop: \"1px solid #e5e7eb\",\n }}\n >\n {pref.description ?? \"\"}\n </div>\n )}\n </div>\n );\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleSaveCustom}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n width: \"100%\",\n }}\n >\n Xác nhận lựa chọn của tôi\n </button>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Browser cookie helpers for phygital-consent.\n * Cookie name for consent preference: phygital_cookie_consent.\n * Stored value: { deviceId: string, selected_preferences: string[] } (JSON).\n * Default: 1 year, path /, SameSite Lax.\n */\n\nexport const COOKIE_CONSENT_NAME = \"phygital_cookie_consent\";\n\nconst ONE_YEAR_SECONDS = 365 * 24 * 60 * 60;\n\nexport interface SetCookieOptions {\n maxAge?: number;\n path?: string;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n secure?: boolean;\n}\n\n/**\n * Get a cookie value by name.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(new RegExp(\"(?:^|; )\" + encodeURIComponent(name) + \"=([^;]*)\"));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Set a cookie.\n */\nexport function setCookie(\n name: string,\n value: string,\n options: SetCookieOptions = {}\n): void {\n if (typeof document === \"undefined\") return;\n const {\n maxAge = ONE_YEAR_SECONDS,\n path = \"/\",\n sameSite = \"Lax\",\n secure = false,\n } = options;\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=${path}; max-age=${maxAge}; SameSite=${sameSite}`;\n if (secure) cookie += \"; Secure\";\n document.cookie = cookie;\n}\n\nexport interface ConsentPreferenceValue {\n deviceId: string;\n selected_preferences: string[];\n}\n\n/**\n * Read consent preference from cookie. Returns null if missing or invalid.\n */\nexport function getConsentPreferenceCookie(): ConsentPreferenceValue | null {\n const raw = getCookie(COOKIE_CONSENT_NAME);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"deviceId\" in parsed &&\n \"selected_preferences\" in parsed &&\n typeof (parsed as ConsentPreferenceValue).deviceId === \"string\" &&\n Array.isArray((parsed as ConsentPreferenceValue).selected_preferences)\n ) {\n return parsed as ConsentPreferenceValue;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\n/**\n * Store consent preference in cookie (hashed device id + selected_preferences).\n * Uses 1 year max-age and SameSite Lax.\n */\nexport function setConsentPreferenceCookie(\n deviceId: string,\n selected_preferences: string[],\n options?: Partial<SetCookieOptions>\n): void {\n const value: ConsentPreferenceValue = { deviceId, selected_preferences };\n setCookie(COOKIE_CONSENT_NAME, JSON.stringify(value), {\n maxAge: ONE_YEAR_SECONDS,\n path: \"/\",\n sameSite: \"Lax\",\n ...options,\n });\n}\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { sha256 } from \"../helpers/sha256\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\n/** Query keys for consent API */\nexport const consentQueryKeys = {\n all: [\"consent\"] as const,\n health: () => [...consentQueryKeys.all, \"health\"] as const,\n list: (params?: GetManyConsentParams) =>\n [...consentQueryKeys.all, \"list\", params] as const,\n detail: (id: string) => [...consentQueryKeys.all, \"detail\", id] as const,\n latestByUserId: (user_id: string) =>\n [...consentQueryKeys.all, \"user-id\", user_id] as const,\n latestCookieByDeviceId: (device_id: string) =>\n [...consentQueryKeys.all, \"cookies\", \"device-id\", device_id] as const,\n};\n\n// --- Queries ---\n\n/** Health check – GET /health */\nexport const useHealth = (\n options?: UseQueryOptions<HealthResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<HealthResponse>({\n queryKey: consentQueryKeys.health(),\n queryFn: () => service.getHealth(),\n ...options,\n });\n};\n\n/** Get all consent logs – GET /consent/v1/consent */\nexport const useManyConsents = (\n params?: GetManyConsentParams,\n options?: UseQueryOptions<GetManyConsentResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetManyConsentResponse>({\n queryKey: consentQueryKeys.list(params),\n queryFn: () => service.getManyConsents(params),\n ...options,\n });\n};\n\n/** Get consent log by ID – GET /consent/v1/consent/{id} */\nexport const useConsentById = (\n params: GetConsentByIdParams,\n options?: UseQueryOptions<GetConsentByIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetConsentByIdResponse>({\n queryKey: consentQueryKeys.detail(params.id),\n queryFn: () => service.getConsentById(params),\n enabled: !!params.id,\n ...options,\n });\n};\n\n/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */\nexport const useLatestConsentByUserId = (\n params: GetLatestConsentByUserIdParams,\n options?: UseQueryOptions<GetLatestConsentByUserIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestConsentByUserIdResponse>({\n queryKey: consentQueryKeys.latestByUserId(params.user_id),\n queryFn: async () => {\n const user_id = await sha256(params.user_id);\n return service.getLatestConsentByUserId({ user_id });\n },\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */\nexport const useLatestCookieConsentByDeviceId = (\n params: GetLatestCookieConsentByDeviceIdParams,\n options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestCookieConsentByDeviceIdResponse>({\n queryKey: consentQueryKeys.latestCookieByDeviceId(params.device_id),\n queryFn: async () => {\n const device_id = await sha256(params.device_id);\n return service.getLatestCookieConsentByDeviceId({ device_id });\n },\n enabled: !!params.device_id,\n ...options,\n });\n};\n\n// --- Mutations ---\n\n/** Create user consent (account register) – POST /consent/v1/user-id */\nexport const useCreateUserConsent = (\n options?: UseMutationOptions<\n CreateUserConsentResponse,\n Error,\n CreateUserConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>({\n mutationFn: async (body: CreateUserConsentUpsertDTO) => {\n const payload = { ...body };\n if (body.user_id != null && body.user_id !== \"\") {\n payload.user_id = await sha256(body.user_id);\n }\n if (body.device_id != null && body.device_id !== \"\") {\n payload.device_id = await sha256(body.device_id);\n }\n return service.createUserConsent(payload);\n },\n onSuccess: (\n _data: CreateUserConsentResponse,\n variables: CreateUserConsentUpsertDTO\n ) => {\n if (variables.user_id) {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestByUserId(variables.user_id),\n });\n }\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n\n/** Create device cookie consent – POST /consent/v1/cookies/device-id */\nexport const useCreateDeviceCookieConsent = (\n options?: UseMutationOptions<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >({\n mutationFn: async (body: CreateDeviceCookieConsentUpsertDTO) => {\n const payload = {\n ...body,\n device_id: await sha256(body.device_id),\n };\n return service.createDeviceCookieConsent(payload);\n },\n onSuccess: (\n _data: CreateDeviceCookieConsentResponse,\n variables: CreateDeviceCookieConsentUpsertDTO\n ) => {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestCookieByDeviceId(variables.device_id),\n });\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n","import { AxiosInstance } from \"axios\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n GetPoliciesParams,\n GetPoliciesResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\nconst baseUrl = \"\";\n\nexport const consentService = (axiosInstance: AxiosInstance) => {\n return {\n /**\n * Health check – GET /health\n */\n getHealth: async (): Promise<HealthResponse> => {\n const { data } = await axiosInstance.get<HealthResponse>(\"/health\");\n return data;\n },\n\n /**\n * Get all consent logs – GET /consent/v1/consent\n * @param params - Optional filter by policy type (ACCOUNT_REGISTER | COOKIE_PREFERENCE)\n */\n getManyConsents: async (\n params?: GetManyConsentParams\n ): Promise<GetManyConsentResponse> => {\n const { data } = await axiosInstance.get<GetManyConsentResponse>(\n `${baseUrl}/consent`,\n { params: params ? { type: params.type } : undefined }\n );\n return data;\n },\n\n /**\n * Get consent log by ID – GET /consent/v1/consent/{id}\n */\n getConsentById: async ({\n id,\n }: GetConsentByIdParams): Promise<GetConsentByIdResponse> => {\n const { data } = await axiosInstance.get<GetConsentByIdResponse>(\n `${baseUrl}/consent/${id}`\n );\n return data;\n },\n\n /**\n * Get policies – GET /consent/v1/policy\n * Optional filters: type (ACCOUNT_REGISTER | COOKIE_PREFERENCE), version, status (ACTIVE | INACTIVE)\n */\n getPolicies: async (params?: GetPoliciesParams): Promise<GetPoliciesResponse> => {\n const { data } = await axiosInstance.get<GetPoliciesResponse>(\n `${baseUrl}/policy`,\n { params: params ?? undefined }\n );\n return data;\n },\n\n /**\n * Get latest consent for a user – GET /consent/v1/user-id?user_id=\n */\n getLatestConsentByUserId: async ({\n user_id,\n }: GetLatestConsentByUserIdParams): Promise<GetLatestConsentByUserIdResponse> => {\n const { data } = await axiosInstance.get<GetLatestConsentByUserIdResponse>(\n `${baseUrl}/user-id`,\n { params: { user_id } }\n );\n return data;\n },\n\n /**\n * Create user consent (account register) – POST /consent/v1/user-id\n * Consumer app must hash user_id/device_id (e.g. SHA-256) before passing.\n */\n createUserConsent: async (\n body: CreateUserConsentUpsertDTO\n ): Promise<CreateUserConsentResponse> => {\n const { data } = await axiosInstance.post<CreateUserConsentResponse>(\n `${baseUrl}/user-id`,\n body\n );\n return data;\n },\n\n /**\n * Get latest cookie consent for a device – GET /consent/v1/cookies/device-id?device_id=\n */\n getLatestCookieConsentByDeviceId: async ({\n device_id,\n }: GetLatestCookieConsentByDeviceIdParams): Promise<GetLatestCookieConsentByDeviceIdResponse> => {\n const { data } =\n await axiosInstance.get<GetLatestCookieConsentByDeviceIdResponse>(\n `${baseUrl}/cookies/device-id`,\n { params: { device_id } }\n );\n return data;\n },\n\n /**\n * Create device cookie consent – POST /consent/v1/cookies/device-id\n * Consumer app must hash device_id (e.g. SHA-256) before passing.\n */\n createDeviceCookieConsent: async (\n body: CreateDeviceCookieConsentUpsertDTO\n ): Promise<CreateDeviceCookieConsentResponse> => {\n const { data } =\n await axiosInstance.post<CreateDeviceCookieConsentResponse>(\n `${baseUrl}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Input: string, encoded as UTF-8 before hashing.\n * Output: lowercase hex string.\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n */\n\n/** Encode hash digest bytes as lowercase hex string. */\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .toLowerCase();\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n * Input encoded as UTF-8; output lowercase hex.\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const utf8 = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", utf8);\n return arrayBufferToHex(hashBuffer);\n}\n\n/**\n * Pure-JS SHA-256 fallback for environments without crypto.subtle\n * (e.g. old browsers, insecure context, Node < 19).\n * Input encoded as UTF-8; output lowercase hex.\n */\nfunction sha256Fallback(value: string): string {\n const bytes = utf8Encode(value);\n const K = getK();\n const H = getH();\n const W = new Uint32Array(64);\n const numBlocks = bytes.length >> 6;\n\n for (let block = 0; block < numBlocks; block++) {\n const start = block << 6;\n for (let i = 0; i < 16; i++) {\n const o = start + (i << 2);\n W[i] =\n (bytes[o]! << 24) |\n (bytes[o + 1]! << 16) |\n (bytes[o + 2]! << 8) |\n bytes[o + 3]!;\n }\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(W[i - 15]!, 7) ^ rotr(W[i - 15]!, 18) ^ (W[i - 15]! >>> 3);\n const s1 = rotr(W[i - 2]!, 17) ^ rotr(W[i - 2]!, 19) ^ (W[i - 2]! >>> 10);\n W[i] = (W[i - 16]! + s0 + W[i - 7]! + s1) >>> 0;\n }\n let a = H[0]!,\n b = H[1]!,\n c = H[2]!,\n d = H[3]!,\n e = H[4]!,\n f = H[5]!,\n g = H[6]!,\n h = H[7]!;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i]! + W[i]!) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0]! + a) >>> 0;\n H[1] = (H[1]! + b) >>> 0;\n H[2] = (H[2]! + c) >>> 0;\n H[3] = (H[3]! + d) >>> 0;\n H[4] = (H[4]! + e) >>> 0;\n H[5] = (H[5]! + f) >>> 0;\n H[6] = (H[6]! + g) >>> 0;\n H[7] = (H[7]! + h) >>> 0;\n }\n\n let out = \"\";\n for (let i = 0; i < 8; i++) {\n const v = H[i]!;\n out +=\n (v >>> 28).toString(16) +\n ((v >>> 24) & 0xf).toString(16) +\n ((v >>> 20) & 0xf).toString(16) +\n ((v >>> 16) & 0xf).toString(16) +\n ((v >>> 12) & 0xf).toString(16) +\n ((v >>> 8) & 0xf).toString(16) +\n ((v >>> 4) & 0xf).toString(16) +\n (v & 0xf).toString(16);\n }\n return out.toLowerCase();\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\n/** Encode string to UTF-8 bytes (with SHA-256 padding for block processing). */\nfunction utf8Encode(s: string): Uint8Array {\n const n = s.length;\n const bytes: number[] = [];\n for (let i = 0; i < n; i++) {\n let c = s.charCodeAt(i);\n if (c < 0x80) {\n bytes.push(c);\n } else if (c < 0x800) {\n bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));\n } else if (c < 0xd800 || c >= 0xe000) {\n bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));\n } else {\n c = 0x10000 + (((c & 0x3ff) << 10) | (s.charCodeAt(++i) & 0x3ff));\n bytes.push(\n 0xf0 | (c >> 18),\n 0x80 | ((c >> 12) & 0x3f),\n 0x80 | ((c >> 6) & 0x3f),\n 0x80 | (c & 0x3f)\n );\n }\n }\n const len = bytes.length;\n const pad = (64 - ((len + 9) % 64)) % 64;\n const total = len + 9 + pad;\n const out = new Uint8Array(total);\n out.set(bytes);\n out[len] = 0x80;\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n view.setUint32(total - 8, 0, false);\n view.setUint32(total - 4, (len * 8) >>> 0, false);\n return out;\n}\n\nfunction getK(): number[] {\n const k: number[] = [];\n const hex =\n \"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2\";\n hex.split(\" \").forEach((h) => k.push(parseInt(h, 16)));\n return k;\n}\n\nfunction getH(): number[] {\n return [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n ];\n}\n\nfunction hasWebCrypto(): boolean {\n if (typeof crypto === \"undefined\" || !crypto.subtle) return false;\n return true;\n}\n\n/**\n * Hash a string with SHA-256.\n * Input: encoded as UTF-8. Output: lowercase hex string.\n * Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.\n * Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).\n */\nexport async function sha256(value: string): Promise<string> {\n if (typeof value !== \"string\" || value.length === 0) {\n return value;\n }\n if (hasWebCrypto()) {\n return sha256WebCrypto(value);\n }\n return Promise.resolve(sha256Fallback(value));\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientConfig, QueryClientProvider } from \"@tanstack/react-query\";\nimport axios, {\n AxiosInstance,\n AxiosRequestConfig,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ConsentService {\n consentApi: AxiosInstance;\n queryClient: QueryClient;\n updateHeaders: (headers: Record<string, string>) => void;\n}\n\nconst ConsentServiceContext = createContext<ConsentService | undefined>(undefined);\n\ninterface RequestInterceptor {\n onFulfilled?: (\n config: InternalAxiosRequestConfig\n ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;\n onRejected?: (error: unknown) => unknown;\n}\n\ninterface ResponseInterceptor {\n onFulfilled?: (\n response: AxiosResponse\n ) => AxiosResponse | Promise<AxiosResponse>;\n onRejected?: (error: unknown) => unknown;\n}\n\nexport interface ConsentServiceProviderProps {\n children: React.ReactNode;\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n queryClient?: QueryClient;\n queryClientConfig?: Partial<QueryClientConfig>;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\ninterface CreateConsentServiceParams {\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\nexport const createConsentService = ({\n baseURL = \"\",\n axiosConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}: CreateConsentServiceParams) => {\n const instance = axios.create({\n baseURL,\n ...axiosConfig,\n });\n\n instance.interceptors.request.use(\n requestInterceptors.onFulfilled,\n requestInterceptors.onRejected\n );\n\n instance.interceptors.response.use(\n responseInterceptors.onFulfilled,\n responseInterceptors.onRejected\n );\n\n const updateHeaders = (headers: Record<string, string>) => {\n Object.entries(headers).forEach(([key, value]) => {\n instance.defaults.headers.common[key] = value;\n });\n };\n\n return {\n consentApi: instance,\n updateHeaders,\n };\n};\n\nexport const ConsentServiceProvider: React.FC<ConsentServiceProviderProps> = ({\n children,\n baseURL = \"\",\n axiosConfig = {},\n queryClient,\n queryClientConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}) => {\n const queryClientInstance = useMemo(\n () =>\n queryClient ||\n new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n ...queryClientConfig?.defaultOptions?.queries,\n },\n mutations: {\n ...queryClientConfig?.defaultOptions?.mutations,\n },\n },\n queryCache: queryClientConfig?.queryCache,\n mutationCache: queryClientConfig?.mutationCache,\n }),\n [queryClient]\n );\n\n const consentService = useMemo(() => {\n const service = createConsentService({\n baseURL,\n axiosConfig,\n requestInterceptors,\n responseInterceptors,\n });\n\n return {\n consentApi: service.consentApi,\n updateHeaders: service.updateHeaders,\n queryClient: queryClientInstance,\n };\n }, [baseURL, queryClientInstance, axiosConfig, requestInterceptors, responseInterceptors]);\n\n return (\n <ConsentServiceContext.Provider value={consentService}>\n <QueryClientProvider client={queryClientInstance}>\n {children}\n </QueryClientProvider>\n </ConsentServiceContext.Provider>\n );\n};\n\nexport const useConsentService = () => {\n const context = useContext(ConsentServiceContext);\n if (!context) {\n throw new Error(\"useConsentService must be used within a ConsentServiceProvider\");\n }\n return context;\n};\n","import {\n useQuery,\n UseQueryOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetPoliciesParams,\n GetPoliciesResponse,\n} from \"../types\";\n\n/** Query keys for policy API */\nexport const policyQueryKeys = {\n all: [\"policy\"] as const,\n list: (params?: GetPoliciesParams) =>\n [...policyQueryKeys.all, \"list\", params] as const,\n};\n\n/** Get policies – GET /consent/v1/policy (optional filters: type, version, status) */\nexport const usePolicies = (\n params?: GetPoliciesParams,\n options?: UseQueryOptions<GetPoliciesResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetPoliciesResponse>({\n queryKey: policyQueryKeys.list(params),\n queryFn: () => service.getPolicies(params),\n ...options,\n });\n};\n","/**\n * Analytics and advertising consent helpers.\n * Cookie preference must exist and include \"analytics\" or \"advertising\" respectively.\n */\n\nimport { getConsentPreferenceCookie } from \"./cookie\";\n\nconst ANALYTICS_PREFERENCE_KEY = \"analytics\";\nconst ADVERTISING_PREFERENCE_KEY = \"advertising\";\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"analytics\" is in selected_preferences. Otherwise GA/GTM/PostHog/OpenReplay are blocked.\n */\nexport function isAnalyticsAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ANALYTICS_PREFERENCE_KEY);\n}\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"advertising\" is in selected_preferences. Otherwise ad tools (e.g. Google AdSense) are blocked.\n */\nexport function isAdvertisingAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ADVERTISING_PREFERENCE_KEY);\n}\n","\"use client\";\n\nimport { isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\n/**\n * Returns whether analytics (GA) is allowed based on cookie preference.\n * Re-renders when consent is updated (e.g. after banner submit).\n * Must be used within PhygitalConsentProvider.\n */\nexport function useIsAnalyticsAllowed(): boolean {\n usePhygitalConsent(); // subscribe to re-renders when refreshConsentPreference is called\n return isAnalyticsAllowed();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useCreateUserConsent } from \"../hooks/useConsent\";\n\nconst POLICY_PLACEHOLDER = `\nTanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.\n`.trim();\n\nconst REFUSAL_MESSAGE =\n \"Vì bạn không đồng ý với thỏa thuận này. Chúng tôi sẽ xóa tài khoản trong vòng x ngày. Nhấn nút 'Đồng ý' để tiếp tục\";\n\nexport interface PolicyPopupProps {\n open: boolean;\n onClose: () => void;\n onReject: () => void;\n userId: string;\n /** Called after user agrees and consent is created successfully. */\n onAgreeSuccess?: () => void;\n}\n\nexport function PolicyPopup({\n open,\n onClose,\n onReject,\n userId,\n onAgreeSuccess,\n}: PolicyPopupProps) {\n const [agreed, setAgreed] = useState(false);\n const [showRefusalAlert, setShowRefusalAlert] = useState(false);\n\n const createUserConsent = useCreateUserConsent({\n onSuccess: (_data, variables) => {\n if (variables.status === \"ACCEPTED\") onAgreeSuccess?.();\n handleClose();\n },\n });\n\n const handleClose = () => {\n setAgreed(false);\n setShowRefusalAlert(false);\n onClose();\n };\n\n const handleRefuse = () => {\n setShowRefusalAlert(true);\n };\n\n const handleBackToPolicy = () => {\n setShowRefusalAlert(false);\n };\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\") => ({\n user_id: userId,\n status,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const handleConfirmReject = () => {\n createUserConsent.mutate(getConsentPayload(\"REJECTED\"));\n onReject?.();\n };\n\n const handleAgree = () => {\n if (!agreed) return;\n createUserConsent.mutate(getConsentPayload(\"ACCEPTED\"));\n };\n\n if (!open) return null;\n\n return (\n <>\n {/* Policy modal */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n }}\n >\n <div\n className=\"consent:overflow-hidden consent:flex consent:flex-col\"\n style={{\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button> */}\n <h2\n style={{\n fontSize: \"18px\",\n fontWeight: 600,\n color: \"#000\",\n padding: \"20px 2.5rem 12px 20px\",\n }}\n >\n Chính sách bảo mật\n </h2>\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: \"1rem 1.25rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n lineHeight: 1.6,\n }}\n >\n <p style={{ margin: 0, whiteSpace: \"pre-wrap\" }}>\n {POLICY_PLACEHOLDER}\n </p>\n </div>\n <div\n style={{\n padding: \"1rem 1.25rem 1.25rem 1.25rem\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={agreed}\n onChange={(e) => setAgreed(e.target.checked)}\n style={{ width: \"1rem\", height: \"1rem\", accentColor: \"#111827\" }}\n aria-label=\"Tôi đồng ý với chính sách bảo mật trên\"\n />\n <span>Tôi đồng ý với chính sách bảo mật trên</span>\n </label>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleRefuse}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Từ chối\n </button>\n <button\n type=\"button\"\n onClick={handleAgree}\n disabled={!agreed || createUserConsent.isPending}\n className=\"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: agreed ? \"#111827\" : \"#e5e7eb\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: agreed ? \"#fff\" : \"#9ca3af\",\n border: \"none\",\n cursor: agreed ? \"pointer\" : \"not-allowed\",\n }}\n >\n Tiếp tục\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: 0 }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Refusal alert modal */}\n {showRefusalAlert && (\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Thông báo từ chối\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 70,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && handleBackToPolicy()}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"22rem\",\n width: \"100%\",\n padding: \"1.25rem\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <p\n className=\"consent:text-sm consent:text-gray-700\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#374151\",\n margin: \"0 0 1rem 0\",\n lineHeight: 1.5,\n }}\n >\n {REFUSAL_MESSAGE}\n </p>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleBackToPolicy}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Trở lại\n </button>\n <button\n type=\"button\"\n onClick={handleConfirmReject}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: \"#a71f24\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Đồng ý\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: \"0.5rem 0 0 0\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n"],"mappings":"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","names":["MediaType","EntityStatus","createContext","useCallback","useContext","useEffect","useState","useMemo","useState","COOKIE_CONSENT_NAME","getCookie","name","match","setCookie","value","options","maxAge","path","sameSite","secure","cookie","getConsentPreferenceCookie","raw","COOKIE_CONSENT_NAME","parsed","setConsentPreferenceCookie","deviceId","selected_preferences","useQuery","useMutation","useQueryClient","baseUrl","consentService","axiosInstance","data","params","id","user_id","body","device_id","arrayBufferToHex","buffer","b","sha256WebCrypto","value","utf8","hashBuffer","sha256Fallback","bytes","utf8Encode","K","getK","H","getH","W","numBlocks","block","start","i","o","s0","rotr","s1","a","c","d","e","f","g","h","S1","ch","t1","S0","maj","t2","out","v","n","s","len","pad","total","view","k","hasWebCrypto","sha256","QueryClient","QueryClientProvider","axios","createContext","useContext","useMemo","jsx","ConsentServiceContext","createConsentService","baseURL","axiosConfig","requestInterceptors","responseInterceptors","instance","headers","key","value","ConsentServiceProvider","children","queryClient","queryClientConfig","queryClientInstance","consentService","service","useConsentService","context","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useQuery","useManyConsents","useConsentById","useLatestConsentByUserId","sha256","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","useQueryClient","useMutation","body","payload","_data","variables","useCreateDeviceCookieConsent","useQuery","policyQueryKeys","params","usePolicies","options","consentApi","useConsentService","service","consentService","useQuery","Fragment","jsx","jsxs","CookieConsentBanner","onSubmitted","onDismiss","className","dismissed","setDismissed","useState","showCustomModal","setShowCustomModal","expandedKey","setExpandedKey","preferences","setPreferences","policyResponse","usePolicies","preferenceList","useMemo","p","effectivePreferences","id","deviceId","usePhygitalConsent","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","getConsentPayload","status","selected_preferences","togglePreference","prev","handleReject","handleAcceptAll","ids","handleSaveCustom","selected","e","pref","checked","isExpanded","disabled","ANALYTICS_PREFERENCE_KEY","ADVERTISING_PREFERENCE_KEY","isAnalyticsAllowed","stored","getConsentPreferenceCookie","isAdvertisingAllowed","jsx","jsxs","PhygitalConsentContext","createContext","PhygitalConsentProviderInner","children","deviceId","showBannerWhenNoPreference","hasConsentPreference","setHasConsentPreference","useState","refreshConsentPreference","useCallback","stored","getConsentPreferenceCookie","useEffect","value","isAnalyticsAllowed","isAdvertisingAllowed","CookieConsentBanner","PhygitalConsentProvider","props","consentServiceProps","ConsentServiceProvider","usePhygitalConsent","context","useContext","useIsAnalyticsAllowed","usePhygitalConsent","isAnalyticsAllowed","useState","Fragment","jsx","jsxs","POLICY_PLACEHOLDER","REFUSAL_MESSAGE","PolicyPopup","open","onClose","onReject","userId","onAgreeSuccess","agreed","setAgreed","useState","showRefusalAlert","setShowRefusalAlert","createUserConsent","useCreateUserConsent","_data","variables","handleClose","handleRefuse","handleBackToPolicy","getConsentPayload","status","handleConfirmReject","handleAgree","e"]}
1
+ {"version":3,"sources":["../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/components/CookieConsentBanner.tsx","../src/helpers/cookie.ts","../src/hooks/useConsent.ts","../src/api/consent.ts","../src/helpers/sha256.ts","../src/provider/ConsentServiceProvider.tsx","../src/hooks/usePolicy.ts","../src/helpers/analytics-consent.ts","../src/hooks/useAnalyticsConsent.ts","../src/components/PolicyPopup.tsx"],"sourcesContent":["export enum MediaType {\n IMAGE = \"image\",\n VIDEO = \"video\",\n}\n\nexport type DateTimeNumber = number;\n\nexport interface Media {\n url: string;\n type: MediaType | string;\n thumbnail_url?: string;\n}\n\nexport enum EntityStatus {\n ACTIVE = \"Active\",\n INACTIVE = \"Inactive\",\n}\n\nexport interface CommonModel {\n id: string;\n status: EntityStatus;\n created_at: DateTimeNumber;\n updated_at: DateTimeNumber;\n created_by?: string;\n updated_by?: string;\n}\n","\"use client\";\n\nimport React, { createContext, useCallback, useContext, useEffect, useState } from \"react\";\nimport { CookieConsentBanner } from \"../components/CookieConsentBanner\";\nimport { isAdvertisingAllowed, isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n deviceId: string;\n hasConsentPreference: boolean;\n isAnalyticsAllowed: boolean;\n isAdvertisingAllowed: boolean;\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n deviceId: string;\n children: React.ReactNode;\n showBannerWhenNoPreference?: boolean;\n}\n\n/** Inner provider: runs inside ConsentServiceProvider so useLatestConsentByUserId has context. */\nfunction PhygitalConsentProviderInner({\n children,\n deviceId,\n showBannerWhenNoPreference = true,\n}: Pick<PhygitalConsentProviderProps, \"children\" | \"deviceId\" | \"showBannerWhenNoPreference\">) {\n const [hasConsentPreference, setHasConsentPreference] = useState(false);\n\n const refreshConsentPreference = useCallback(() => {\n const stored = getConsentPreferenceCookie();\n setHasConsentPreference(!!stored);\n }, []);\n\n useEffect(() => {\n refreshConsentPreference();\n }, [refreshConsentPreference]);\n\n const value: PhygitalConsentContextValue = {\n deviceId,\n hasConsentPreference,\n isAnalyticsAllowed: isAnalyticsAllowed(),\n isAdvertisingAllowed: isAdvertisingAllowed(),\n refreshConsentPreference,\n };\n\n return (\n <PhygitalConsentContext.Provider value={value}>\n {children}\n {showBannerWhenNoPreference && !hasConsentPreference && (\n <CookieConsentBanner onSubmitted={refreshConsentPreference} />\n )}\n </PhygitalConsentContext.Provider>\n );\n}\n\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, deviceId, showBannerWhenNoPreference = true, ...consentServiceProps } = props;\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentProviderInner\n deviceId={deviceId}\n showBannerWhenNoPreference={showBannerWhenNoPreference}\n >\n {children}\n </PhygitalConsentProviderInner>\n </ConsentServiceProvider>\n );\n}\n\nexport function usePhygitalConsent() {\n const context = useContext(PhygitalConsentContext);\n if (!context) {\n throw new Error(\"usePhygitalConsent must be used within a PhygitalConsentProvider\");\n }\n return context;\n}\n","\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { COOKIE_POLICY_URL, OTHER_INFO_URL } from \"../env/constant\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\nimport { usePolicies } from \"../hooks/usePolicy\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\nexport interface CookieConsentBannerProps {\n /** Called after consent is submitted successfully (Reject or Accept). */\n onSubmitted?: () => void;\n /** Called when the banner is dismissed (e.g. hide from UI). */\n onDismiss?: () => void;\n /** Optional class name for the root container. */\n className?: string;\n}\n\nexport function CookieConsentBanner({\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [showCustomModal, setShowCustomModal] = useState(false);\n const [expandedKey, setExpandedKey] = useState<string | null>(null);\n const [preferences, setPreferences] = useState<Record<string, boolean>>({});\n\n const { data: policyResponse } = usePolicies({ type: \"COOKIE_PREFERENCE\", status: \"ACTIVE\" });\n\n const policy = useMemo(\n () => {\n if (!policyResponse?.data) return null;\n\n return policyResponse?.data?.find((p) => p.type === \"COOKIE_PREFERENCE\" && p.status === \"ACTIVE\") ?? policyResponse?.data?.[0];\n },\n [policyResponse]\n );\n const preferenceList = policy?.preferences ?? [];\n\n const effectivePreferences = useMemo(() => {\n const defaultAllTrue = Object.fromEntries(\n preferenceList.map((p) => [p.id ?? \"\", true]).filter(([id]) => !!id)\n );\n return { ...defaultAllTrue, ...preferences };\n }, [preferenceList, preferences]);\n\n const { deviceId } = usePhygitalConsent();\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n setShowCustomModal(false);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n preferenceList\n .map((p) => p.id)\n .filter((id): id is string => !!id)\n .filter((id) => effectivePreferences[id]);\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\", selected_preferences: string[]) => ({\n device_id: deviceId,\n status,\n selected_preferences,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const togglePreference = (id: string) => {\n const pref = preferenceList.find((p) => p.id === id);\n if (pref?.is_mandatory) return;\n setPreferences((prev) => ({ ...prev, [id]: !effectivePreferences[id] }));\n };\n\n const handleReject = () => {\n createConsent.mutate(getConsentPayload(\"REJECTED\", []));\n };\n\n const handleAcceptAll = () => {\n const ids = preferenceList.map((p) => p.id).filter((id): id is string => !!id);\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", ids.length > 0 ? ids : []));\n };\n\n const handleSaveCustom = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate(getConsentPayload(\"ACCEPTED\", selected.length > 0 ? selected : [\"essential\"]));\n };\n\n if (dismissed) return null;\n\n return (\n <>\n {/* Screen 1: Bottom banner */}\n <div\n role=\"dialog\"\n aria-label=\"Thông báo về cookie\"\n className={`consent:fixed consent:bottom-0 consent:left-0 consent:right-0 consent:z-50 consent:flex consent:flex-col consent:gap-4 consent:rounded-t-xl consent:border consent:border-b-0 consent:border-gray-200 consent:bg-white consent:p-4 consent:shadow-lg consent:md:left-4 consent:md:right-auto consent:md:bottom-4 consent:md:max-w-md consent:md:rounded-xl consent:md:border consent:md:border-gray-200 ${className}`}\n style={{\n position: \"fixed\",\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 50,\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n borderRadius: \"0.75rem 0.75rem 0 0\",\n borderWidth: \"1px 1px 0 1px\",\n borderColor: \"#e5e7eb\",\n backgroundColor: \"#fff\",\n padding: \"1rem\",\n boxShadow: \"0 -4px 6px -1px rgb(0 0 0 / 0.1)\",\n }}\n >\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\" }}\n >\n Thông báo về cookie\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\" }}\n >\n <p>\n TapQuest sử dụng trình theo dõi (như Cookie hoặc SDK) cho:\n </p>\n <ul style={{ margin: 0, paddingLeft: \"1.25rem\" }}>\n {preferenceList.map((p) => (\n <li key={p.id ?? p.name}>{p.name ?? p.id ?? \"\"}</li>\n ))}\n </ul>\n <p>\n Trước khi bạn đồng ý, chỉ những trình theo dõi hoàn toàn cần thiết mới được triển khai.\n </p>\n <p>\n Nếu bạn chọn Chấp nhận tất cả, bạn đồng ý sử dụng tất cả trình theo dõi của chúng tôi.\n </p>\n <p>\n Nếu bạn chọn Từ chối tất cả, bạn từ chối những trình theo dõi yêu cầu sự đồng ý của bạn và sẽ không có quyền truy cập vào các đề nghị hoặc nội dung được cá nhân hóa.\n </p>\n <p>\n Nếu bạn chọn Tùy chọn, bạn có thể chọn xem bạn có đồng ý sử dụng trình theo dõi trên ứng dụng của chúng tôi hay không và ở mức độ nào.\n </p>\n <p>\n Bạn có thể rút lại sự đồng ý của mình bất kỳ lúc nào bằng cách nhấp vào liên kết trong{\" \"}\n {COOKIE_POLICY_URL ? (\n <a\n href={COOKIE_POLICY_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Chính sách cookie\n </a>\n ) : (\n \"Chính sách cookie\"\n )}{\" \"}\n của chúng tôi.\n </p>\n </div>\n <div className=\"flex justify-end gap-2 flex-wrap\">\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(true)}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Tùy chọn\n </button>\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Từ chối tất cả\n </button>\n <button\n type=\"button\"\n onClick={handleAcceptAll}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Chấp nhận tất cả\n </button>\n </div>\n {createConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n\n {/* Screen 2: Custom preferences modal */}\n {showCustomModal && (\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Trung tâm tuỳ chọn bảo mật\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 60,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && setShowCustomModal(false)}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg consent:max-h-[90vh] consent:overflow-y-auto\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"28rem\",\n width: \"100%\",\n maxHeight: \"90vh\",\n overflowY: \"auto\",\n padding: \"1.25rem\",\n position: \"relative\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <button\n type=\"button\"\n onClick={() => setShowCustomModal(false)}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button>\n <h2\n className=\"consent:text-lg consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"1.125rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\", paddingRight: \"2rem\" }}\n >\n Trung tâm tuỳ chọn bảo mật\n </h2>\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", marginBottom: \"1rem\" }}\n >\n <p style={{ marginBottom: \"0.5rem\" }}>\n Khi bạn truy cập bất kỳ trang web nào, trang web đó có thể lưu trữ hoặc truy xuất thông tin trên trình duyệt của bạn, chủ yếu dưới dạng cookie. Thông tin này có thể liên quan đến bạn, tùy chọn của bạn hoặc thiết bị của bạn, và chủ yếu được sử dụng để trang web hoạt động như mong đợi.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Thông tin này thường không trực tiếp xác định bạn nhưng có thể mang lại trải nghiệm web được cá nhân hóa hơn. Chúng tôi tôn trọng quyền riêng tư của bạn; bạn có thể chọn không cho phép một số loại cookie. Nhấp vào tiêu đề từng danh mục để tìm hiểu thêm và thay đổi cài đặt mặc định. Việc chặn một số cookie có thể ảnh hưởng đến trải nghiệm của bạn với trang web và các dịch vụ được cung cấp.\n </p>\n <p style={{ marginBottom: \"0.5rem\" }}>\n Nếu chọn \"Không theo dõi\" khi lần đầu sử dụng TapQuest, chỉ cookie hoàn toàn cần thiết mới được sử dụng.\n </p>\n {OTHER_INFO_URL ? (\n <a\n href={OTHER_INFO_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"consent:text-blue-600 consent:underline\"\n style={{ color: \"#2563eb\", textDecoration: \"underline\" }}\n >\n Thông tin khác\n </a>\n ) : (\n <span style={{ color: \"#2563eb\", textDecoration: \"underline\" }}>Thông tin khác</span>\n )}\n </div>\n <h3\n className=\"consent:text-sm consent:font-semibold consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", fontWeight: 600, color: \"#111827\", marginBottom: \"0.75rem\" }}\n >\n Quản lý tuỳ chọn đồng ý\n </h3>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", marginBottom: \"1.25rem\" }}>\n {preferenceList.map((pref) => {\n const id = pref.id ?? pref.name ?? \"\";\n const checked = effectivePreferences[id];\n const isExpanded = expandedKey === id;\n const disabled = pref.is_mandatory === true;\n return (\n <div\n key={id}\n style={{\n border: \"1px solid #e5e7eb\",\n borderRadius: \"0.5rem\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n cursor: \"pointer\",\n backgroundColor: isExpanded ? \"#f9fafb\" : \"transparent\",\n }}\n onClick={() => setExpandedKey(isExpanded ? null : id)}\n >\n <span\n style={{\n flexShrink: 0,\n width: \"1.25rem\",\n height: \"1.25rem\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: \"1rem\",\n color: \"#6b7280\",\n transform: isExpanded ? \"rotate(45deg)\" : \"none\",\n transition: \"transform 0.2s\",\n }}\n >\n +\n </span>\n <span\n className=\"consent:font-medium consent:text-gray-900\"\n style={{ flex: 1, fontSize: \"0.875rem\", fontWeight: 500, color: \"#111827\" }}\n >\n {pref.name ?? id}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${pref.name ?? id} ${checked ? \"bật\" : \"tắt\"}`}\n disabled={disabled}\n onClick={(e) => {\n e.stopPropagation();\n togglePreference(id);\n }}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: disabled ? \"default\" : \"pointer\",\n backgroundColor: disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: disabled ? 0.7 : 1,\n transition: \"background-color 0.2s\",\n }}\n >\n <span\n style={{\n display: \"block\",\n width: 20,\n height: 20,\n borderRadius: \"50%\",\n backgroundColor: \"#fff\",\n marginLeft: checked ? 22 : 2,\n marginTop: 2,\n transition: \"margin-left 0.2s\",\n boxShadow: \"0 1px 2px rgb(0 0 0 / 0.2)\",\n }}\n />\n </button>\n </div>\n {isExpanded && (\n <div\n className=\"consent:text-sm consent:text-gray-600\"\n style={{\n fontSize: \"0.875rem\",\n color: \"#4b5563\",\n padding: \"0.75rem 0.75rem 0.75rem 2.5rem\",\n borderTop: \"1px solid #e5e7eb\",\n }}\n >\n {pref.description ?? \"\"}\n </div>\n )}\n </div>\n );\n })}\n </div>\n <button\n type=\"button\"\n onClick={handleSaveCustom}\n disabled={createConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50 consent:w-full\"\n style={{\n borderRadius: \"0.5rem\",\n background: \"#111827\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n width: \"100%\",\n }}\n >\n Xác nhận lựa chọn của tôi\n </button>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Browser cookie helpers for phygital-consent.\n * Cookie name for consent preference: phygital_cookie_consent.\n * Stored value: { deviceId: string, selected_preferences: string[] } (JSON).\n * Default: 1 year, path /, SameSite Lax.\n */\n\nexport const COOKIE_CONSENT_NAME = \"phygital_cookie_consent\";\n\nconst ONE_YEAR_SECONDS = 365 * 24 * 60 * 60;\n\nexport interface SetCookieOptions {\n maxAge?: number;\n path?: string;\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\n secure?: boolean;\n}\n\n/**\n * Get a cookie value by name.\n */\nexport function getCookie(name: string): string | null {\n if (typeof document === \"undefined\") return null;\n const match = document.cookie.match(new RegExp(\"(?:^|; )\" + encodeURIComponent(name) + \"=([^;]*)\"));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Set a cookie.\n */\nexport function setCookie(\n name: string,\n value: string,\n options: SetCookieOptions = {}\n): void {\n if (typeof document === \"undefined\") return;\n const {\n maxAge = ONE_YEAR_SECONDS,\n path = \"/\",\n sameSite = \"Lax\",\n secure = false,\n } = options;\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=${path}; max-age=${maxAge}; SameSite=${sameSite}`;\n if (secure) cookie += \"; Secure\";\n document.cookie = cookie;\n}\n\nexport interface ConsentPreferenceValue {\n deviceId: string;\n selected_preferences: string[];\n}\n\n/**\n * Read consent preference from cookie. Returns null if missing or invalid.\n */\nexport function getConsentPreferenceCookie(): ConsentPreferenceValue | null {\n const raw = getCookie(COOKIE_CONSENT_NAME);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"deviceId\" in parsed &&\n \"selected_preferences\" in parsed &&\n typeof (parsed as ConsentPreferenceValue).deviceId === \"string\" &&\n Array.isArray((parsed as ConsentPreferenceValue).selected_preferences)\n ) {\n return parsed as ConsentPreferenceValue;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\n/**\n * Store consent preference in cookie (hashed device id + selected_preferences).\n * Uses 1 year max-age and SameSite Lax.\n */\nexport function setConsentPreferenceCookie(\n deviceId: string,\n selected_preferences: string[],\n options?: Partial<SetCookieOptions>\n): void {\n const value: ConsentPreferenceValue = { deviceId, selected_preferences };\n setCookie(COOKIE_CONSENT_NAME, JSON.stringify(value), {\n maxAge: ONE_YEAR_SECONDS,\n path: \"/\",\n sameSite: \"Lax\",\n ...options,\n });\n}\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { sha256 } from \"../helpers/sha256\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\n/** Query keys for consent API */\nexport const consentQueryKeys = {\n all: [\"consent\"] as const,\n health: () => [...consentQueryKeys.all, \"health\"] as const,\n list: (params?: GetManyConsentParams) =>\n [...consentQueryKeys.all, \"list\", params] as const,\n detail: (id: string) => [...consentQueryKeys.all, \"detail\", id] as const,\n latestByUserId: (user_id: string) =>\n [...consentQueryKeys.all, \"user-id\", user_id] as const,\n latestCookieByDeviceId: (device_id: string) =>\n [...consentQueryKeys.all, \"cookies\", \"device-id\", device_id] as const,\n};\n\n// --- Queries ---\n\n/** Health check – GET /health */\nexport const useHealth = (\n options?: UseQueryOptions<HealthResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<HealthResponse>({\n queryKey: consentQueryKeys.health(),\n queryFn: () => service.getHealth(),\n ...options,\n });\n};\n\n/** Get all consent logs – GET /consent/v1/consent */\nexport const useManyConsents = (\n params?: GetManyConsentParams,\n options?: UseQueryOptions<GetManyConsentResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetManyConsentResponse>({\n queryKey: consentQueryKeys.list(params),\n queryFn: () => service.getManyConsents(params),\n ...options,\n });\n};\n\n/** Get consent log by ID – GET /consent/v1/consent/{id} */\nexport const useConsentById = (\n params: GetConsentByIdParams,\n options?: UseQueryOptions<GetConsentByIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetConsentByIdResponse>({\n queryKey: consentQueryKeys.detail(params.id),\n queryFn: () => service.getConsentById(params),\n enabled: !!params.id,\n ...options,\n });\n};\n\n/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */\nexport const useLatestConsentByUserId = (\n params: GetLatestConsentByUserIdParams,\n options?: UseQueryOptions<GetLatestConsentByUserIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestConsentByUserIdResponse>({\n queryKey: consentQueryKeys.latestByUserId(params.user_id),\n queryFn: async () => {\n const user_id = await sha256(params.user_id);\n return service.getLatestConsentByUserId({ user_id });\n },\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */\nexport const useLatestCookieConsentByDeviceId = (\n params: GetLatestCookieConsentByDeviceIdParams,\n options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetLatestCookieConsentByDeviceIdResponse>({\n queryKey: consentQueryKeys.latestCookieByDeviceId(params.device_id),\n queryFn: async () => {\n const device_id = await sha256(params.device_id);\n return service.getLatestCookieConsentByDeviceId({ device_id });\n },\n enabled: !!params.device_id,\n ...options,\n });\n};\n\n// --- Mutations ---\n\n/** Create user consent (account register) – POST /consent/v1/user-id */\nexport const useCreateUserConsent = (\n options?: UseMutationOptions<\n CreateUserConsentResponse,\n Error,\n CreateUserConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>({\n mutationFn: async (body: CreateUserConsentUpsertDTO) => {\n const payload = { ...body };\n if (body.user_id != null && body.user_id !== \"\") {\n payload.user_id = await sha256(body.user_id);\n }\n if (body.device_id != null && body.device_id !== \"\") {\n payload.device_id = await sha256(body.device_id);\n }\n return service.createUserConsent(payload);\n },\n onSuccess: (\n _data: CreateUserConsentResponse,\n variables: CreateUserConsentUpsertDTO\n ) => {\n if (variables.user_id) {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestByUserId(variables.user_id),\n });\n }\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n\n/** Create device cookie consent – POST /consent/v1/cookies/device-id */\nexport const useCreateDeviceCookieConsent = (\n options?: UseMutationOptions<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >\n) => {\n const { consentApi } = useConsentService();\n const queryClient = useQueryClient();\n const service = consentService(consentApi);\n\n return useMutation<\n CreateDeviceCookieConsentResponse,\n Error,\n CreateDeviceCookieConsentUpsertDTO\n >({\n mutationFn: async (body: CreateDeviceCookieConsentUpsertDTO) => {\n const payload = {\n ...body,\n device_id: await sha256(body.device_id),\n };\n return service.createDeviceCookieConsent(payload);\n },\n onSuccess: (\n _data: CreateDeviceCookieConsentResponse,\n variables: CreateDeviceCookieConsentUpsertDTO\n ) => {\n queryClient.invalidateQueries({\n queryKey: consentQueryKeys.latestCookieByDeviceId(variables.device_id),\n });\n queryClient.invalidateQueries({ queryKey: consentQueryKeys.list() });\n },\n ...options,\n });\n};\n","import { AxiosInstance } from \"axios\";\nimport type {\n GetManyConsentParams,\n GetManyConsentResponse,\n GetConsentByIdParams,\n GetConsentByIdResponse,\n GetLatestConsentByUserIdParams,\n GetLatestConsentByUserIdResponse,\n GetLatestCookieConsentByDeviceIdParams,\n GetLatestCookieConsentByDeviceIdResponse,\n GetPoliciesParams,\n GetPoliciesResponse,\n CreateUserConsentUpsertDTO,\n CreateUserConsentResponse,\n CreateDeviceCookieConsentUpsertDTO,\n CreateDeviceCookieConsentResponse,\n HealthResponse,\n} from \"../types\";\n\nconst baseUrl = \"\";\n\nexport const consentService = (axiosInstance: AxiosInstance) => {\n return {\n /**\n * Health check – GET /health\n */\n getHealth: async (): Promise<HealthResponse> => {\n const { data } = await axiosInstance.get<HealthResponse>(\"/health\");\n return data;\n },\n\n /**\n * Get all consent logs – GET /consent/v1/consent\n * @param params - Optional filter by policy type (ACCOUNT_REGISTER | COOKIE_PREFERENCE)\n */\n getManyConsents: async (\n params?: GetManyConsentParams\n ): Promise<GetManyConsentResponse> => {\n const { data } = await axiosInstance.get<GetManyConsentResponse>(\n `${baseUrl}/consent`,\n { params: params ? { type: params.type } : undefined }\n );\n return data;\n },\n\n /**\n * Get consent log by ID – GET /consent/v1/consent/{id}\n */\n getConsentById: async ({\n id,\n }: GetConsentByIdParams): Promise<GetConsentByIdResponse> => {\n const { data } = await axiosInstance.get<GetConsentByIdResponse>(\n `${baseUrl}/consent/${id}`\n );\n return data;\n },\n\n /**\n * Get policies – GET /consent/v1/policy\n * Optional filters: type (ACCOUNT_REGISTER | COOKIE_PREFERENCE), version, status (ACTIVE | INACTIVE)\n */\n getPolicies: async (params?: GetPoliciesParams): Promise<GetPoliciesResponse> => {\n const { data } = await axiosInstance.get<GetPoliciesResponse>(\n `${baseUrl}/policy`,\n { params: params ?? undefined }\n );\n return data;\n },\n\n /**\n * Get latest consent for a user – GET /consent/v1/user-id?user_id=\n */\n getLatestConsentByUserId: async ({\n user_id,\n }: GetLatestConsentByUserIdParams): Promise<GetLatestConsentByUserIdResponse> => {\n const { data } = await axiosInstance.get<GetLatestConsentByUserIdResponse>(\n `${baseUrl}/user-id`,\n { params: { user_id } }\n );\n return data;\n },\n\n /**\n * Create user consent (account register) – POST /consent/v1/user-id\n * Consumer app must hash user_id/device_id (e.g. SHA-256) before passing.\n */\n createUserConsent: async (\n body: CreateUserConsentUpsertDTO\n ): Promise<CreateUserConsentResponse> => {\n const { data } = await axiosInstance.post<CreateUserConsentResponse>(\n `${baseUrl}/user-id`,\n body\n );\n return data;\n },\n\n /**\n * Get latest cookie consent for a device – GET /consent/v1/cookies/device-id?device_id=\n */\n getLatestCookieConsentByDeviceId: async ({\n device_id,\n }: GetLatestCookieConsentByDeviceIdParams): Promise<GetLatestCookieConsentByDeviceIdResponse> => {\n const { data } =\n await axiosInstance.get<GetLatestCookieConsentByDeviceIdResponse>(\n `${baseUrl}/cookies/device-id`,\n { params: { device_id } }\n );\n return data;\n },\n\n /**\n * Create device cookie consent – POST /consent/v1/cookies/device-id\n * Consumer app must hash device_id (e.g. SHA-256) before passing.\n */\n createDeviceCookieConsent: async (\n body: CreateDeviceCookieConsentUpsertDTO\n ): Promise<CreateDeviceCookieConsentResponse> => {\n const { data } =\n await axiosInstance.post<CreateDeviceCookieConsentResponse>(\n `${baseUrl}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Input: string, encoded as UTF-8 before hashing.\n * Output: lowercase hex string.\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n */\n\n/** Encode hash digest bytes as lowercase hex string. */\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .toLowerCase();\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n * Input encoded as UTF-8; output lowercase hex.\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const utf8 = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", utf8);\n return arrayBufferToHex(hashBuffer);\n}\n\n/**\n * Pure-JS SHA-256 fallback for environments without crypto.subtle\n * (e.g. old browsers, insecure context, Node < 19).\n * Input encoded as UTF-8; output lowercase hex.\n */\nfunction sha256Fallback(value: string): string {\n const bytes = utf8Encode(value);\n const K = getK();\n const H = getH();\n const W = new Uint32Array(64);\n const numBlocks = bytes.length >> 6;\n\n for (let block = 0; block < numBlocks; block++) {\n const start = block << 6;\n for (let i = 0; i < 16; i++) {\n const o = start + (i << 2);\n W[i] =\n (bytes[o]! << 24) |\n (bytes[o + 1]! << 16) |\n (bytes[o + 2]! << 8) |\n bytes[o + 3]!;\n }\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(W[i - 15]!, 7) ^ rotr(W[i - 15]!, 18) ^ (W[i - 15]! >>> 3);\n const s1 = rotr(W[i - 2]!, 17) ^ rotr(W[i - 2]!, 19) ^ (W[i - 2]! >>> 10);\n W[i] = (W[i - 16]! + s0 + W[i - 7]! + s1) >>> 0;\n }\n let a = H[0]!,\n b = H[1]!,\n c = H[2]!,\n d = H[3]!,\n e = H[4]!,\n f = H[5]!,\n g = H[6]!,\n h = H[7]!;\n for (let i = 0; i < 64; i++) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const t1 = (h + S1 + ch + K[i]! + W[i]!) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const t2 = (S0 + maj) >>> 0;\n h = g;\n g = f;\n f = e;\n e = (d + t1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (t1 + t2) >>> 0;\n }\n H[0] = (H[0]! + a) >>> 0;\n H[1] = (H[1]! + b) >>> 0;\n H[2] = (H[2]! + c) >>> 0;\n H[3] = (H[3]! + d) >>> 0;\n H[4] = (H[4]! + e) >>> 0;\n H[5] = (H[5]! + f) >>> 0;\n H[6] = (H[6]! + g) >>> 0;\n H[7] = (H[7]! + h) >>> 0;\n }\n\n let out = \"\";\n for (let i = 0; i < 8; i++) {\n const v = H[i]!;\n out +=\n (v >>> 28).toString(16) +\n ((v >>> 24) & 0xf).toString(16) +\n ((v >>> 20) & 0xf).toString(16) +\n ((v >>> 16) & 0xf).toString(16) +\n ((v >>> 12) & 0xf).toString(16) +\n ((v >>> 8) & 0xf).toString(16) +\n ((v >>> 4) & 0xf).toString(16) +\n (v & 0xf).toString(16);\n }\n return out.toLowerCase();\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\n/** Encode string to UTF-8 bytes (with SHA-256 padding for block processing). */\nfunction utf8Encode(s: string): Uint8Array {\n const n = s.length;\n const bytes: number[] = [];\n for (let i = 0; i < n; i++) {\n let c = s.charCodeAt(i);\n if (c < 0x80) {\n bytes.push(c);\n } else if (c < 0x800) {\n bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));\n } else if (c < 0xd800 || c >= 0xe000) {\n bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));\n } else {\n c = 0x10000 + (((c & 0x3ff) << 10) | (s.charCodeAt(++i) & 0x3ff));\n bytes.push(\n 0xf0 | (c >> 18),\n 0x80 | ((c >> 12) & 0x3f),\n 0x80 | ((c >> 6) & 0x3f),\n 0x80 | (c & 0x3f)\n );\n }\n }\n const len = bytes.length;\n const pad = (64 - ((len + 9) % 64)) % 64;\n const total = len + 9 + pad;\n const out = new Uint8Array(total);\n out.set(bytes);\n out[len] = 0x80;\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n view.setUint32(total - 8, 0, false);\n view.setUint32(total - 4, (len * 8) >>> 0, false);\n return out;\n}\n\nfunction getK(): number[] {\n const k: number[] = [];\n const hex =\n \"428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5 d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174 e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85 a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2\";\n hex.split(\" \").forEach((h) => k.push(parseInt(h, 16)));\n return k;\n}\n\nfunction getH(): number[] {\n return [\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,\n 0x1f83d9ab, 0x5be0cd19,\n ];\n}\n\nfunction hasWebCrypto(): boolean {\n if (typeof crypto === \"undefined\" || !crypto.subtle) return false;\n return true;\n}\n\n/**\n * Hash a string with SHA-256.\n * Input: encoded as UTF-8. Output: lowercase hex string.\n * Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.\n * Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).\n */\nexport async function sha256(value: string): Promise<string> {\n if (typeof value !== \"string\" || value.length === 0) {\n return value;\n }\n if (hasWebCrypto()) {\n return sha256WebCrypto(value);\n }\n return Promise.resolve(sha256Fallback(value));\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientConfig, QueryClientProvider } from \"@tanstack/react-query\";\nimport axios, {\n AxiosInstance,\n AxiosRequestConfig,\n AxiosResponse,\n InternalAxiosRequestConfig,\n} from \"axios\";\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nexport interface ConsentService {\n consentApi: AxiosInstance;\n queryClient: QueryClient;\n updateHeaders: (headers: Record<string, string>) => void;\n}\n\nconst ConsentServiceContext = createContext<ConsentService | undefined>(undefined);\n\ninterface RequestInterceptor {\n onFulfilled?: (\n config: InternalAxiosRequestConfig\n ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>;\n onRejected?: (error: unknown) => unknown;\n}\n\ninterface ResponseInterceptor {\n onFulfilled?: (\n response: AxiosResponse\n ) => AxiosResponse | Promise<AxiosResponse>;\n onRejected?: (error: unknown) => unknown;\n}\n\nexport interface ConsentServiceProviderProps {\n children: React.ReactNode;\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n queryClient?: QueryClient;\n queryClientConfig?: Partial<QueryClientConfig>;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\ninterface CreateConsentServiceParams {\n baseURL?: string;\n axiosConfig?: AxiosRequestConfig;\n requestInterceptors?: RequestInterceptor;\n responseInterceptors?: ResponseInterceptor;\n}\n\nexport const createConsentService = ({\n baseURL = \"\",\n axiosConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}: CreateConsentServiceParams) => {\n const instance = axios.create({\n baseURL,\n ...axiosConfig,\n });\n\n instance.interceptors.request.use(\n requestInterceptors.onFulfilled,\n requestInterceptors.onRejected\n );\n\n instance.interceptors.response.use(\n responseInterceptors.onFulfilled,\n responseInterceptors.onRejected\n );\n\n const updateHeaders = (headers: Record<string, string>) => {\n Object.entries(headers).forEach(([key, value]) => {\n instance.defaults.headers.common[key] = value;\n });\n };\n\n return {\n consentApi: instance,\n updateHeaders,\n };\n};\n\nexport const ConsentServiceProvider: React.FC<ConsentServiceProviderProps> = ({\n children,\n baseURL = \"\",\n axiosConfig = {},\n queryClient,\n queryClientConfig = {},\n requestInterceptors = {},\n responseInterceptors = {},\n}) => {\n const queryClientInstance = useMemo(\n () =>\n queryClient ||\n new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n ...queryClientConfig?.defaultOptions?.queries,\n },\n mutations: {\n ...queryClientConfig?.defaultOptions?.mutations,\n },\n },\n queryCache: queryClientConfig?.queryCache,\n mutationCache: queryClientConfig?.mutationCache,\n }),\n [queryClient]\n );\n\n const consentService = useMemo(() => {\n const service = createConsentService({\n baseURL,\n axiosConfig,\n requestInterceptors,\n responseInterceptors,\n });\n\n return {\n consentApi: service.consentApi,\n updateHeaders: service.updateHeaders,\n queryClient: queryClientInstance,\n };\n }, [baseURL, queryClientInstance, axiosConfig, requestInterceptors, responseInterceptors]);\n\n return (\n <ConsentServiceContext.Provider value={consentService}>\n <QueryClientProvider client={queryClientInstance}>\n {children}\n </QueryClientProvider>\n </ConsentServiceContext.Provider>\n );\n};\n\nexport const useConsentService = () => {\n const context = useContext(ConsentServiceContext);\n if (!context) {\n throw new Error(\"useConsentService must be used within a ConsentServiceProvider\");\n }\n return context;\n};\n","import {\n useQuery,\n UseQueryOptions,\n} from \"@tanstack/react-query\";\nimport { consentService } from \"../api/consent\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport type {\n GetPoliciesParams,\n GetPoliciesResponse,\n} from \"../types\";\n\n/** Query keys for policy API */\nexport const policyQueryKeys = {\n all: [\"policy\"] as const,\n list: (params?: GetPoliciesParams) =>\n [...policyQueryKeys.all, \"list\", params] as const,\n};\n\n/** Get policies – GET /consent/v1/policy (optional filters: type, version, status) */\nexport const usePolicies = (\n params?: GetPoliciesParams,\n options?: UseQueryOptions<GetPoliciesResponse>\n) => {\n const { consentApi } = useConsentService();\n const service = consentService(consentApi);\n\n return useQuery<GetPoliciesResponse>({\n queryKey: policyQueryKeys.list(params),\n queryFn: () => service.getPolicies(params),\n ...options,\n });\n};\n","/**\n * Analytics and advertising consent helpers.\n * Cookie preference must exist and include \"analytics\" or \"advertising\" respectively.\n */\n\nimport { getConsentPreferenceCookie } from \"./cookie\";\n\nconst ANALYTICS_PREFERENCE_KEY = \"analytics\";\nconst ADVERTISING_PREFERENCE_KEY = \"advertising\";\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"analytics\" is in selected_preferences. Otherwise GA/GTM/PostHog/OpenReplay are blocked.\n */\nexport function isAnalyticsAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ANALYTICS_PREFERENCE_KEY);\n}\n\n/**\n * Returns true only when user has submitted cookie preference and\n * \"advertising\" is in selected_preferences. Otherwise ad tools (e.g. Google AdSense) are blocked.\n */\nexport function isAdvertisingAllowed(): boolean {\n const stored = getConsentPreferenceCookie();\n if (!stored?.selected_preferences) return false;\n return stored.selected_preferences.includes(ADVERTISING_PREFERENCE_KEY);\n}\n","\"use client\";\n\nimport { isAnalyticsAllowed } from \"../helpers/analytics-consent\";\nimport { usePhygitalConsent } from \"../provider/PhygitalConsentProvider\";\n\n/**\n * Returns whether analytics (GA) is allowed based on cookie preference.\n * Re-renders when consent is updated (e.g. after banner submit).\n * Must be used within PhygitalConsentProvider.\n */\nexport function useIsAnalyticsAllowed(): boolean {\n usePhygitalConsent(); // subscribe to re-renders when refreshConsentPreference is called\n return isAnalyticsAllowed();\n}\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useCreateUserConsent } from \"../hooks/useConsent\";\n\nconst POLICY_PLACEHOLDER = `\nTanta petere igitur et tam longe abesse non opus est. Quodsi haberent magnalia inter potentiam et divitias, et tamen ista philosophia et disciplina vivendi. Quam ob rem ut illi superiores, nos usque ad hanc aetatem. Sed ut iis bonis erudiamur, quae gignuntur ex tempore. Et tamen rerum necessitatibus saepe vincimur. Quam ob rem ut illi superiores, nos usque ad hanc aetatem.\n`.trim();\n\nconst REFUSAL_MESSAGE =\n \"Vì bạn không đồng ý với thỏa thuận này. Chúng tôi sẽ xóa tài khoản trong vòng x ngày. Nhấn nút 'Đồng ý' để tiếp tục\";\n\nexport interface PolicyPopupProps {\n open: boolean;\n onClose: () => void;\n onReject: () => void;\n userId: string;\n /** Called after user agrees and consent is created successfully. */\n onAgreeSuccess?: () => void;\n}\n\nexport function PolicyPopup({\n open,\n onClose,\n onReject,\n userId,\n onAgreeSuccess,\n}: PolicyPopupProps) {\n const [agreed, setAgreed] = useState(false);\n const [showRefusalAlert, setShowRefusalAlert] = useState(false);\n\n const createUserConsent = useCreateUserConsent({\n onSuccess: (_data, variables) => {\n if (variables.status === \"ACCEPTED\") onAgreeSuccess?.();\n handleClose();\n },\n });\n\n const handleClose = () => {\n setAgreed(false);\n setShowRefusalAlert(false);\n onClose();\n };\n\n const handleRefuse = () => {\n setShowRefusalAlert(true);\n };\n\n const handleBackToPolicy = () => {\n setShowRefusalAlert(false);\n };\n\n const getConsentPayload = (status: \"REJECTED\" | \"ACCEPTED\") => ({\n user_id: userId,\n status,\n page_url: typeof window !== \"undefined\" ? window.location.href : undefined,\n user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : undefined,\n });\n\n const handleConfirmReject = () => {\n createUserConsent.mutate(getConsentPayload(\"REJECTED\"));\n onReject?.();\n };\n\n const handleAgree = () => {\n if (!agreed) return;\n createUserConsent.mutate(getConsentPayload(\"ACCEPTED\"));\n };\n\n if (!open) return null;\n\n return (\n <>\n {/* Policy modal */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <div\n className=\"consent:overflow-hidden consent:flex consent:flex-col\"\n style={{\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Đóng\"\n style={{\n position: \"absolute\",\n top: \"1rem\",\n right: \"1rem\",\n width: \"2rem\",\n height: \"2rem\",\n borderRadius: \"0.25rem\",\n border: \"none\",\n background: \"transparent\",\n cursor: \"pointer\",\n fontSize: \"1.25rem\",\n lineHeight: 1,\n color: \"#6b7280\",\n }}\n >\n ×\n </button> */}\n <h2\n style={{\n fontSize: \"20px\",\n fontWeight: 600,\n color: \"#000\",\n padding: \"20px 2.5rem 12px 20px\",\n }}\n >\n Chính sách bảo mật\n </h2>\n <div\n style={{\n flex: 1,\n overflowY: \"auto\",\n padding: \"1rem 1.25rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n lineHeight: 1.6,\n }}\n >\n <p style={{ margin: 0, whiteSpace: \"pre-wrap\" }}>\n {POLICY_PLACEHOLDER}\n </p>\n </div>\n <div\n style={{\n padding: \"1rem 1.25rem 1.25rem 1.25rem\",\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <label\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"0.5rem\",\n fontSize: \"0.875rem\",\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n <input\n type=\"checkbox\"\n checked={agreed}\n onChange={(e) => setAgreed(e.target.checked)}\n style={{ width: \"1rem\", height: \"1rem\", accentColor: \"#111827\" }}\n aria-label=\"Tôi đồng ý với chính sách bảo mật trên\"\n />\n <span>Tôi đồng ý với chính sách bảo mật trên</span>\n </label>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleRefuse}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Từ chối\n </button>\n <button\n type=\"button\"\n onClick={handleAgree}\n disabled={!agreed || createUserConsent.isPending}\n className=\"consent:rounded-lg consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:shadow-sm disabled:consent:opacity-50 disabled:consent:cursor-not-allowed\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: agreed ? \"#111827\" : \"#e5e7eb\",\n padding: \"0.5rem 1rem\",\n fontSize: \"1rem\",\n fontWeight: 500,\n color: agreed ? \"#fff\" : \"#9ca3af\",\n border: \"none\",\n cursor: agreed ? \"pointer\" : \"not-allowed\",\n }}\n >\n Tiếp tục\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"1rem\", color: \"#dc2626\", margin: 0 }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Refusal alert modal */}\n {showRefusalAlert && (\n <div\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Thông báo từ chối\"\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: 70,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n backgroundColor: \"rgba(0,0,0,0.5)\",\n padding: \"1rem\",\n }}\n onClick={(e) => e.target === e.currentTarget && handleBackToPolicy()}\n >\n <div\n className=\"consent:bg-white consent:rounded-xl consent:border consent:border-gray-200 consent:shadow-lg\"\n style={{\n backgroundColor: \"#fff\",\n borderRadius: \"0.75rem\",\n border: \"1px solid #e5e7eb\",\n boxShadow: \"0 10px 15px -3px rgb(0 0 0 / 0.1)\",\n maxWidth: \"22rem\",\n width: \"100%\",\n padding: \"1.25rem\",\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <p\n className=\"consent:text-sm consent:text-gray-700\"\n style={{\n fontSize: \"1rem\",\n color: \"#374151\",\n margin: \"0 0 1rem 0\",\n lineHeight: 1.5,\n }}\n >\n {REFUSAL_MESSAGE}\n </p>\n <div\n style={{\n display: \"flex\",\n gap: \"0.5rem\",\n flexWrap: \"wrap\",\n }}\n >\n <button\n type=\"button\"\n onClick={handleBackToPolicy}\n className=\"consent:rounded-lg consent:border consent:border-gray-300 consent:bg-white consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-gray-700 consent:shadow-sm hover:consent:bg-gray-50 disabled:consent:opacity-50\"\n style={{\n flex: 1,\n borderRadius: \"0.5rem\",\n border: \"1px solid #111827\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#111827\",\n cursor: \"pointer\",\n }}\n >\n Trở lại\n </button>\n <button\n type=\"button\"\n onClick={handleConfirmReject}\n disabled={createUserConsent.isPending}\n className=\"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm disabled:consent:opacity-50 consent:flex-1\"\n style={{\n flex: 1,\n minWidth: 0,\n borderRadius: \"0.5rem\",\n background: \"#a71f24\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n }}\n >\n Đồng ý\n </button>\n </div>\n {createUserConsent.isError && (\n <p\n className=\"consent:text-sm consent:text-red-600\"\n style={{ fontSize: \"0.875rem\", color: \"#dc2626\", margin: \"0.5rem 0 0 0\" }}\n >\n Đã xảy ra lỗi. Vui lòng thử lại.\n </p>\n )}\n </div>\n </div>\n )}\n </>\n );\n}\n"],"mappings":"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","names":["MediaType","EntityStatus","createContext","useCallback","useContext","useEffect","useState","useMemo","useState","COOKIE_CONSENT_NAME","getCookie","name","match","setCookie","value","options","maxAge","path","sameSite","secure","cookie","getConsentPreferenceCookie","raw","COOKIE_CONSENT_NAME","parsed","setConsentPreferenceCookie","deviceId","selected_preferences","useQuery","useMutation","useQueryClient","baseUrl","consentService","axiosInstance","data","params","id","user_id","body","device_id","arrayBufferToHex","buffer","b","sha256WebCrypto","value","utf8","hashBuffer","sha256Fallback","bytes","utf8Encode","K","getK","H","getH","W","numBlocks","block","start","i","o","s0","rotr","s1","a","c","d","e","f","g","h","S1","ch","t1","S0","maj","t2","out","v","n","s","len","pad","total","view","k","hasWebCrypto","sha256","QueryClient","QueryClientProvider","axios","createContext","useContext","useMemo","jsx","ConsentServiceContext","createConsentService","baseURL","axiosConfig","requestInterceptors","responseInterceptors","instance","headers","key","value","ConsentServiceProvider","children","queryClient","queryClientConfig","queryClientInstance","consentService","service","useConsentService","context","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useQuery","useManyConsents","useConsentById","useLatestConsentByUserId","sha256","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","useQueryClient","useMutation","body","payload","_data","variables","useCreateDeviceCookieConsent","useQuery","policyQueryKeys","params","usePolicies","options","consentApi","useConsentService","service","consentService","useQuery","Fragment","jsx","jsxs","CookieConsentBanner","onSubmitted","onDismiss","className","dismissed","setDismissed","useState","showCustomModal","setShowCustomModal","expandedKey","setExpandedKey","preferences","setPreferences","policyResponse","usePolicies","preferenceList","useMemo","p","effectivePreferences","id","deviceId","usePhygitalConsent","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","getConsentPayload","status","selected_preferences","togglePreference","prev","handleReject","handleAcceptAll","ids","handleSaveCustom","selected","e","pref","checked","isExpanded","disabled","ANALYTICS_PREFERENCE_KEY","ADVERTISING_PREFERENCE_KEY","isAnalyticsAllowed","stored","getConsentPreferenceCookie","isAdvertisingAllowed","jsx","jsxs","PhygitalConsentContext","createContext","PhygitalConsentProviderInner","children","deviceId","showBannerWhenNoPreference","hasConsentPreference","setHasConsentPreference","useState","refreshConsentPreference","useCallback","stored","getConsentPreferenceCookie","useEffect","value","isAnalyticsAllowed","isAdvertisingAllowed","CookieConsentBanner","PhygitalConsentProvider","props","consentServiceProps","ConsentServiceProvider","usePhygitalConsent","context","useContext","useIsAnalyticsAllowed","usePhygitalConsent","isAnalyticsAllowed","useState","Fragment","jsx","jsxs","POLICY_PLACEHOLDER","REFUSAL_MESSAGE","PolicyPopup","open","onClose","onReject","userId","onAgreeSuccess","agreed","setAgreed","useState","showRefusalAlert","setShowRefusalAlert","createUserConsent","useCreateUserConsent","_data","variables","handleClose","handleRefuse","handleBackToPolicy","getConsentPayload","status","handleConfirmReject","handleAgree","e"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phygitallabs/phygital-consent",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -77,7 +77,6 @@ export function PolicyPopup({
77
77
  display: "flex",
78
78
  alignItems: "center",
79
79
  justifyContent: "center",
80
- padding: "1rem",
81
80
  }}
82
81
  >
83
82
  <div
@@ -114,7 +113,7 @@ export function PolicyPopup({
114
113
  </button> */}
115
114
  <h2
116
115
  style={{
117
- fontSize: "18px",
116
+ fontSize: "20px",
118
117
  fontWeight: 600,
119
118
  color: "#000",
120
119
  padding: "20px 2.5rem 12px 20px",
@@ -200,7 +199,7 @@ export function PolicyPopup({
200
199
  borderRadius: "0.5rem",
201
200
  background: agreed ? "#111827" : "#e5e7eb",
202
201
  padding: "0.5rem 1rem",
203
- fontSize: "0.875rem",
202
+ fontSize: "1rem",
204
203
  fontWeight: 500,
205
204
  color: agreed ? "#fff" : "#9ca3af",
206
205
  border: "none",
@@ -213,7 +212,7 @@ export function PolicyPopup({
213
212
  {createUserConsent.isError && (
214
213
  <p
215
214
  className="consent:text-sm consent:text-red-600"
216
- style={{ fontSize: "0.875rem", color: "#dc2626", margin: 0 }}
215
+ style={{ fontSize: "1rem", color: "#dc2626", margin: 0 }}
217
216
  >
218
217
  Đã xảy ra lỗi. Vui lòng thử lại.
219
218
  </p>
@@ -256,7 +255,7 @@ export function PolicyPopup({
256
255
  <p
257
256
  className="consent:text-sm consent:text-gray-700"
258
257
  style={{
259
- fontSize: "0.875rem",
258
+ fontSize: "1rem",
260
259
  color: "#374151",
261
260
  margin: "0 0 1rem 0",
262
261
  lineHeight: 1.5,