@phygitallabs/phygital-consent 1.0.0 → 1.0.2
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/bun.lock +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/phygital-consent.css +10 -13
- package/package.json +1 -1
- package/src/components/CookieConsentBanner.tsx +103 -24
- package/src/helpers/index.ts +1 -0
- package/src/helpers/sha256.ts +170 -0
- package/src/hooks/useConsent.ts +29 -8
- package/src/provider/PhygitalConsentProvider.tsx +1 -1
package/bun.lock
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
"use strict";var N=Object.create;var v=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var Q=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var F=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},U=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of T(t))!H.call(e,o)&&o!==n&&v(e,o,{get:()=>t[o],enumerable:!(s=M(t,o))||s.enumerable});return e};var V=(e,t,n)=>(n=e!=null?N(Q(e)):{},U(t||!e||!e.__esModule?v(n,"default",{value:e,enumerable:!0}):n,e)),$=e=>U(v({},"__esModule",{value:!0}),e);var se={};F(se,{COOKIE_CONSENT_NAME:()=>P,CookieConsentBanner:()=>ne,EntityStatus:()=>B,MediaType:()=>S,PhygitalConsentProvider:()=>z,consentQueryKeys:()=>a,consentService:()=>u,getConsentPreferenceCookie:()=>h,getCookie:()=>O,setConsentPreferenceCookie:()=>b,setCookie:()=>A,useConsentById:()=>J,useCreateDeviceCookieConsent:()=>D,useCreateUserConsent:()=>ee,useHealth:()=>j,useLatestConsentByUserId:()=>X,useLatestCookieConsentByDeviceId:()=>Z,useManyConsents:()=>Y,usePhygitalConsent:()=>W});module.exports=$(se);var S=(n=>(n.IMAGE="image",n.VIDEO="video",n))(S||{}),B=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(B||{});var p=require("react");var P="phygital_cookie_consent";function O(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function A(e,t,n={}){if(typeof document>"u")return;let{maxAge:s=31536e3,path:o="/",sameSite:r="Lax",secure:c=!1}=n,d=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${o}; max-age=${s}; SameSite=${r}`;c&&(d+="; Secure"),document.cookie=d}function h(){let e=O(P);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 b(e,t,n){A(P,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}var x=require("@tanstack/react-query"),G=V(require("axios")),m=require("react"),k=require("react/jsx-runtime"),w=(0,m.createContext)(void 0),K=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:s={}})=>{let o=G.default.create({baseURL:e,...t});return o.interceptors.request.use(n.onFulfilled,n.onRejected),o.interceptors.response.use(s.onFulfilled,s.onRejected),{consentApi:o,updateHeaders:c=>{Object.entries(c).forEach(([d,g])=>{o.defaults.headers.common[d]=g})}}},E=({children:e,baseURL:t="",axiosConfig:n={},queryClient:s,queryClientConfig:o={},requestInterceptors:r={},responseInterceptors:c={}})=>{let d=(0,m.useMemo)(()=>s||new x.QueryClient({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...o?.defaultOptions?.queries},mutations:{...o?.defaultOptions?.mutations}},queryCache:o?.queryCache,mutationCache:o?.mutationCache}),[s]),g=(0,m.useMemo)(()=>{let R=K({baseURL:t,axiosConfig:n,requestInterceptors:r,responseInterceptors:c});return{consentApi:R.consentApi,updateHeaders:R.updateHeaders,queryClient:d}},[t,d,n,r,c]);return(0,k.jsx)(w.Provider,{value:g,children:(0,k.jsx)(x.QueryClientProvider,{client:d,children:e})})},y=()=>{let e=(0,m.useContext)(w);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};var I=require("react/jsx-runtime"),L=(0,p.createContext)(void 0);function z(e){let{children:t,...n}=e,[s,o]=(0,p.useState)(!1),r=(0,p.useCallback)(()=>{let d=h();o(!!d)},[]);(0,p.useEffect)(()=>{r()},[r]);let c={hasConsentPreference:s,refreshConsentPreference:r};return(0,I.jsx)(E,{...n,children:(0,I.jsx)(L.Provider,{value:c,children:t})})}function W(){let e=(0,p.useContext)(L);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var f="/consent/v1",u=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${f}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${f}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${f}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${f}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${f}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${f}/cookies/device-id`,t);return n}});var C=require("@tanstack/react-query");var a={all:["consent"],health:()=>[...a.all,"health"],list:e=>[...a.all,"list",e],detail:e=>[...a.all,"detail",e],latestByUserId:e=>[...a.all,"user-id",e],latestCookieByDeviceId:e=>[...a.all,"cookies","device-id",e]},j=e=>{let{consentApi:t}=y(),n=u(t);return(0,C.useQuery)({queryKey:a.health(),queryFn:()=>n.getHealth(),...e})},Y=(e,t)=>{let{consentApi:n}=y(),s=u(n);return(0,C.useQuery)({queryKey:a.list(e),queryFn:()=>s.getManyConsents(e),...t})},J=(e,t)=>{let{consentApi:n}=y(),s=u(n);return(0,C.useQuery)({queryKey:a.detail(e.id),queryFn:()=>s.getConsentById(e),enabled:!!e.id,...t})},X=(e,t)=>{let{consentApi:n}=y(),s=u(n);return(0,C.useQuery)({queryKey:a.latestByUserId(e.user_id),queryFn:()=>s.getLatestConsentByUserId(e),enabled:!!e.user_id,...t})},Z=(e,t)=>{let{consentApi:n}=y(),s=u(n);return(0,C.useQuery)({queryKey:a.latestCookieByDeviceId(e.device_id),queryFn:()=>s.getLatestCookieConsentByDeviceId(e),enabled:!!e.device_id,...t})},ee=e=>{let{consentApi:t}=y(),n=(0,C.useQueryClient)(),s=u(t);return(0,C.useMutation)({mutationFn:o=>s.createUserConsent(o),onSuccess:(o,r)=>{r.user_id&&n.invalidateQueries({queryKey:a.latestByUserId(r.user_id)}),n.invalidateQueries({queryKey:a.list()})},...e})},D=e=>{let{consentApi:t}=y(),n=(0,C.useQueryClient)(),s=u(t);return(0,C.useMutation)({mutationFn:o=>s.createDeviceCookieConsent(o),onSuccess:(o,r)=>{n.invalidateQueries({queryKey:a.latestCookieByDeviceId(r.device_id)}),n.invalidateQueries({queryKey:a.list()})},...e})};var _=require("react");var i=require("react/jsx-runtime"),te=["essential","analytics","advertising"],l={title:"Cookie preferences",description:"We use cookies to improve your experience, remember your settings, and understand how you use our site. You can accept all, or reject non-essential cookies.",essentialLabel:"Essential",essentialDesc:"Required for the site to work (e.g. security, preferences).",analyticsLabel:"Analytics",analyticsDesc:"Help us improve by collecting anonymous usage data.",advertisingLabel:"Advertising",advertisingDesc:"Used to show you relevant ads and measure campaigns."};function ne({deviceId:e,onSubmitted:t,onDismiss:n,className:s=""}){let[o,r]=(0,_.useState)(!1),c=D({onSuccess:(R,q)=>{b(e,q.selected_preferences??[]),t?.(),n?.(),r(!0)}}),d=()=>{c.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},g=()=>{c.mutate({device_id:e,status:"ACCEPTED",selected_preferences:[...te]})};return o?null:(0,i.jsxs)("div",{role:"dialog","aria-label":l.title,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 ${s}`,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,i.jsx)("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:l.title}),(0,i.jsx)("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:l.description}),(0,i.jsxs)("ul",{className:"consent:text-sm consent:text-gray-600 consent:space-y-2",style:{fontSize:"0.875rem",color:"#4b5563",margin:0,paddingLeft:"1.25rem"},children:[(0,i.jsxs)("li",{children:[(0,i.jsx)("strong",{children:l.essentialLabel})," \u2014 ",l.essentialDesc]}),(0,i.jsxs)("li",{children:[(0,i.jsx)("strong",{children:l.analyticsLabel})," \u2014 ",l.analyticsDesc]}),(0,i.jsxs)("li",{children:[(0,i.jsx)("strong",{children:l.advertisingLabel})," \u2014 ",l.advertisingDesc]})]}),(0,i.jsxs)("div",{className:"consent:flex consent:flex-wrap consent:gap-2",style:{display:"flex",flexWrap:"wrap",gap:"0.5rem"},children:[(0,i.jsx)("button",{type:"button",onClick:d,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:{borderRadius:"0.5rem",border:"1px solid #d1d5db",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#374151"},children:"Reject"}),(0,i.jsx)("button",{type:"button",onClick:g,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 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"},children:"Accept"})]}),c.isError&&(0,i.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"Something went wrong. Please try again."})]})}0&&(module.exports={COOKIE_CONSENT_NAME,CookieConsentBanner,EntityStatus,MediaType,PhygitalConsentProvider,consentQueryKeys,consentService,getConsentPreferenceCookie,getCookie,setConsentPreferenceCookie,setCookie,useConsentById,useCreateDeviceCookieConsent,useCreateUserConsent,useHealth,useLatestConsentByUserId,useLatestCookieConsentByDeviceId,useManyConsents,usePhygitalConsent});
|
|
2
|
+
"use strict";var te=Object.create;var D=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var se=Object.getPrototypeOf,re=Object.prototype.hasOwnProperty;var ie=(e,t)=>{for(var n in t)D(e,n,{get:t[n],enumerable:!0})},H=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of oe(t))!re.call(e,s)&&s!==n&&D(e,s,{get:()=>t[s],enumerable:!(o=ne(t,s))||o.enumerable});return e};var ae=(e,t,n)=>(n=e!=null?te(se(e)):{},H(t||!e||!e.__esModule?D(n,"default",{value:e,enumerable:!0}):n,e)),ce=e=>H(D({},"__esModule",{value:!0}),e);var Ie={};ie(Ie,{COOKIE_CONSENT_NAME:()=>O,CookieConsentBanner:()=>Se,EntityStatus:()=>F,MediaType:()=>Q,PhygitalConsentProvider:()=>le,consentQueryKeys:()=>C,consentService:()=>b,getConsentPreferenceCookie:()=>_,getCookie:()=>K,setConsentPreferenceCookie:()=>E,setCookie:()=>$,sha256:()=>S,useConsentById:()=>he,useCreateDeviceCookieConsent:()=>q,useCreateUserConsent:()=>ke,useHealth:()=>be,useLatestConsentByUserId:()=>Re,useLatestCookieConsentByDeviceId:()=>Pe,useManyConsents:()=>ve,usePhygitalConsent:()=>ue});module.exports=ce(Ie);var Q=(n=>(n.IMAGE="image",n.VIDEO="video",n))(Q||{}),F=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(F||{});var m=require("react");var O="phygital_cookie_consent";function K(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function $(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:i="Lax",secure:l=!1}=n,c=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${i}`;l&&(c+="; Secure"),document.cookie=c}function _(){let e=K(O);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 E(e,t,n){$(O,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}var w=require("@tanstack/react-query"),V=ae(require("axios")),k=require("react"),G=require("react/jsx-runtime"),W=(0,k.createContext)(void 0),de=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=V.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:l=>{Object.entries(l).forEach(([c,a])=>{s.defaults.headers.common[c]=a})}}},z=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:i={},responseInterceptors:l={}})=>{let c=(0,k.useMemo)(()=>o||new w.QueryClient({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),a=(0,k.useMemo)(()=>{let r=de({baseURL:t,axiosConfig:n,requestInterceptors:i,responseInterceptors:l});return{consentApi:r.consentApi,updateHeaders:r.updateHeaders,queryClient:c}},[t,c,n,i,l]);return(0,G.jsx)(W.Provider,{value:a,children:(0,G.jsx)(w.QueryClientProvider,{client:c,children:e})})},h=()=>{let e=(0,k.useContext)(W);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};var N=require("react/jsx-runtime"),j=(0,m.createContext)(void 0);function le(e){let{children:t,...n}=e,[o,s]=(0,m.useState)(!1),i=(0,m.useCallback)(()=>{let c=_();s(!!c)},[]);(0,m.useEffect)(()=>{i()},[i]);let l={hasConsentPreference:o,refreshConsentPreference:i};return(0,N.jsx)(z,{...n,children:(0,N.jsx)(j.Provider,{value:l,children:t})})}function ue(){let e=(0,m.useContext)(j);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var I="/consent/v1",b=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${I}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${I}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${I}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${I}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${I}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${I}/cookies/device-id`,t);return n}});var y=require("@tanstack/react-query");function pe(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function fe(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return pe(n)}function Ce(e){let t=ye(e),n=me(),o=ge(),s=new Uint32Array(64),i=t.length>>6;for(let c=0;c<i;c++){let a=c<<6;for(let u=0;u<16;u++){let v=a+(u<<2);s[u]=t[v]<<24|t[v+1]<<16|t[v+2]<<8|t[v+3]}for(let u=16;u<64;u++){let v=g(s[u-15],7)^g(s[u-15],18)^s[u-15]>>>3,B=g(s[u-2],17)^g(s[u-2],19)^s[u-2]>>>10;s[u]=s[u-16]+v+s[u-7]+B>>>0}let r=o[0],R=o[1],P=o[2],U=o[3],d=o[4],p=o[5],x=o[6],A=o[7];for(let u=0;u<64;u++){let v=g(d,6)^g(d,11)^g(d,25),B=d&p^~d&x,M=A+v+B+n[u]+s[u]>>>0,X=g(r,2)^g(r,13)^g(r,22),Z=r&R^r&P^R&P,ee=X+Z>>>0;A=x,x=p,p=d,d=U+M>>>0,U=P,P=R,R=r,r=M+ee>>>0}o[0]=o[0]+r>>>0,o[1]=o[1]+R>>>0,o[2]=o[2]+P>>>0,o[3]=o[3]+U>>>0,o[4]=o[4]+d>>>0,o[5]=o[5]+p>>>0,o[6]=o[6]+x>>>0,o[7]=o[7]+A>>>0}let l="";for(let c=0;c<8;c++){let a=o[c];l+=(a>>>28).toString(16)+(a>>>24&15).toString(16)+(a>>>20&15).toString(16)+(a>>>16&15).toString(16)+(a>>>12&15).toString(16)+(a>>>8&15).toString(16)+(a>>>4&15).toString(16)+(a&15).toString(16)}return l}function g(e,t){return e>>>t|e<<32-t}function ye(e){let t=e.length,n=[];for(let a=0;a<t;a++){let r=e.charCodeAt(a);r<128?n.push(r):r<2048?n.push(192|r>>6,128|r&63):r<55296||r>=57344?n.push(224|r>>12,128|r>>6&63,128|r&63):(r=65536+((r&1023)<<10|e.charCodeAt(++a)&1023),n.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63))}let o=n.length,s=(64-(o+9)%64)%64,i=o+9+s,l=new Uint8Array(i);l.set(n),l[o]=128;let c=new DataView(l.buffer,l.byteOffset,l.byteLength);return c.setUint32(i-8,0,!1),c.setUint32(i-4,o*8>>>0,!1),l}function me(){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 ge(){return[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]}function xe(){return!(typeof crypto>"u"||!crypto.subtle)}async function S(e){return typeof e!="string"||e.length===0?e:xe()?fe(e):Promise.resolve(Ce(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]},be=e=>{let{consentApi:t}=h(),n=b(t);return(0,y.useQuery)({queryKey:C.health(),queryFn:()=>n.getHealth(),...e})},ve=(e,t)=>{let{consentApi:n}=h(),o=b(n);return(0,y.useQuery)({queryKey:C.list(e),queryFn:()=>o.getManyConsents(e),...t})},he=(e,t)=>{let{consentApi:n}=h(),o=b(n);return(0,y.useQuery)({queryKey:C.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},Re=(e,t)=>{let{consentApi:n}=h(),o=b(n);return(0,y.useQuery)({queryKey:C.latestByUserId(e.user_id),queryFn:async()=>{let s=await S(e.user_id);return o.getLatestConsentByUserId({user_id:s})},enabled:!!e.user_id,...t})},Pe=(e,t)=>{let{consentApi:n}=h(),o=b(n);return(0,y.useQuery)({queryKey:C.latestCookieByDeviceId(e.device_id),queryFn:async()=>{let s=await S(e.device_id);return o.getLatestCookieConsentByDeviceId({device_id:s})},enabled:!!e.device_id,...t})},ke=e=>{let{consentApi:t}=h(),n=(0,y.useQueryClient)(),o=b(t);return(0,y.useMutation)({mutationFn:async s=>{let i={...s};return s.user_id!=null&&s.user_id!==""&&(i.user_id=await S(s.user_id)),s.device_id!=null&&s.device_id!==""&&(i.device_id=await S(s.device_id)),o.createUserConsent(i)},onSuccess:(s,i)=>{i.user_id&&n.invalidateQueries({queryKey:C.latestByUserId(i.user_id)}),n.invalidateQueries({queryKey:C.list()})},...e})},q=e=>{let{consentApi:t}=h(),n=(0,y.useQueryClient)(),o=b(t);return(0,y.useMutation)({mutationFn:async s=>{let i={...s,device_id:await S(s.device_id)};return o.createDeviceCookieConsent(i)},onSuccess:(s,i)=>{n.invalidateQueries({queryKey:C.latestCookieByDeviceId(i.device_id)}),n.invalidateQueries({queryKey:C.list()})},...e})};var T=require("react");var f=require("react/jsx-runtime"),J=["essential","analytics","advertising"],Y={essential:{label:"Essential",description:"Required for the site to work (e.g. security, preferences)."},analytics:{label:"Analytics",description:"Help us improve by collecting anonymous usage data."},advertising:{label:"Advertising",description:"Used to show you relevant ads and measure campaigns."}},L={title:"Cookie preferences",description:"We use cookies to improve your experience. Choose which categories to enable, then save."};function Se({deviceId:e,onSubmitted:t,onDismiss:n,className:o=""}){let[s,i]=(0,T.useState)(!1),[l,c]=(0,T.useState)({essential:!0,analytics:!1,advertising:!1}),a=q({onSuccess:(d,p)=>{E(e,p.selected_preferences??[]),t?.(),n?.(),i(!0)}}),r=()=>J.filter(d=>l[d]),R=d=>{Y[d].disabled||c(p=>({...p,[d]:!p[d]}))},P=()=>{a.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},U=()=>{let d=r();a.mutate({device_id:e,status:"ACCEPTED",selected_preferences:d.length>0?d:[]})};return s?null:(0,f.jsxs)("div",{role:"dialog","aria-label":L.title,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 ${o}`,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,f.jsx)("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:L.title}),(0,f.jsx)("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:L.description}),(0,f.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:J.map(d=>{let p=Y[d],x=l[d];return(0,f.jsxs)("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"0.75rem"},children:[(0,f.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,f.jsx)("strong",{className:"consent:text-sm consent:text-gray-900",style:{fontSize:"0.875rem",color:"#111827"},children:p.label}),(0,f.jsx)("p",{className:"consent:text-xs consent:text-gray-500",style:{fontSize:"0.75rem",color:"#6b7280",margin:"0.25rem 0 0"},children:p.description})]}),(0,f.jsx)("button",{type:"button",role:"switch","aria-checked":x,"aria-label":`${p.label} cookies ${x?"on":"off"}`,disabled:p.disabled,onClick:()=>R(d),style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:p.disabled?"default":"pointer",backgroundColor:p.disabled?"#e5e7eb":x?"#111827":"#d1d5db",opacity:p.disabled?.7:1,transition:"background-color 0.2s"},children:(0,f.jsx)("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:x?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]},d)})}),(0,f.jsxs)("div",{className:"consent:flex consent:flex-wrap consent:gap-2",style:{display:"flex",flexWrap:"wrap",gap:"0.5rem"},children:[(0,f.jsx)("button",{type:"button",onClick:P,disabled:a.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:{borderRadius:"0.5rem",border:"1px solid #d1d5db",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#374151"},children:"Reject"}),(0,f.jsx)("button",{type:"button",onClick:U,disabled:a.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"},children:"Accept"})]}),a.isError&&(0,f.jsx)("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"Something went wrong. Please try again."})]})}0&&(module.exports={COOKIE_CONSENT_NAME,CookieConsentBanner,EntityStatus,MediaType,PhygitalConsentProvider,consentQueryKeys,consentService,getConsentPreferenceCookie,getCookie,setConsentPreferenceCookie,setCookie,sha256,useConsentById,useCreateDeviceCookieConsent,useCreateUserConsent,useHealth,useLatestConsentByUserId,useLatestCookieConsentByDeviceId,useManyConsents,usePhygitalConsent});
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts","../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/helpers/cookie.ts","../src/provider/ConsentServiceProvider.tsx","../src/api/consent.ts","../src/hooks/useConsent.ts","../src/components/CookieConsentBanner.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 { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n /** True when cookie phygital_cookie_consent exists and has valid deviceId + selected_preferences. Use to show/hide cookie banner. */\n hasConsentPreference: boolean;\n /** Re-read cookie and update hasConsentPreference. Call after storing preference (e.g. from CookieConsentBanner onSuccess). */\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n children: React.ReactNode;\n}\n\n/**\n * Main provider for consumer apps. Wraps ConsentServiceProvider and exposes\n * hasConsentPreference + refreshConsentPreference so the app can show/hide\n * the cookie banner based on whether the user has already chosen a preference.\n */\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, ...consentServiceProps } = props;\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 hasConsentPreference,\n refreshConsentPreference,\n };\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentContext.Provider value={value}>\n {children}\n </PhygitalConsentContext.Provider>\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","/**\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","\"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 { AxiosInstance } from \"axios\";\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\nconst CONSENT_V1 = \"/consent/v1\";\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 `${CONSENT_V1}/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 `${CONSENT_V1}/consent/${id}`\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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport { consentService } from \"../api/consent\";\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 */\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: () => service.getLatestConsentByUserId(params),\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id */\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: () => service.getLatestCookieConsentByDeviceId(params),\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: (body: CreateUserConsentUpsertDTO) => service.createUserConsent(body),\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: (body: CreateDeviceCookieConsentUpsertDTO) =>\n service.createDeviceCookieConsent(body),\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","\"use client\";\n\nimport { useState } from \"react\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\n\nconst PREFERENCES = [\"essential\", \"analytics\", \"advertising\"] as const;\n\nconst MOCK_CONTENT = {\n title: \"Cookie preferences\",\n description:\n \"We use cookies to improve your experience, remember your settings, and understand how you use our site. You can accept all, or reject non-essential cookies.\",\n essentialLabel: \"Essential\",\n essentialDesc: \"Required for the site to work (e.g. security, preferences).\",\n analyticsLabel: \"Analytics\",\n analyticsDesc: \"Help us improve by collecting anonymous usage data.\",\n advertisingLabel: \"Advertising\",\n advertisingDesc: \"Used to show you relevant ads and measure campaigns.\",\n};\n\nexport interface CookieConsentBannerProps {\n /** Device ID (consumer app must provide; will be hashed by app before API if required). */\n deviceId: string;\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 deviceId,\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n },\n });\n\n const handleReject = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"REJECTED\",\n selected_preferences: [\"essential\"],\n });\n };\n\n const handleAccept = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"ACCEPTED\",\n selected_preferences: [...PREFERENCES],\n });\n };\n\n if (dismissed) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-label={MOCK_CONTENT.title}\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 {MOCK_CONTENT.title}\n </h2>\n <p\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\" }}\n >\n {MOCK_CONTENT.description}\n </p>\n <ul\n className=\"consent:text-sm consent:text-gray-600 consent:space-y-2\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", margin: 0, paddingLeft: \"1.25rem\" }}\n >\n <li>\n <strong>{MOCK_CONTENT.essentialLabel}</strong> — {MOCK_CONTENT.essentialDesc}\n </li>\n <li>\n <strong>{MOCK_CONTENT.analyticsLabel}</strong> — {MOCK_CONTENT.analyticsDesc}\n </li>\n <li>\n <strong>{MOCK_CONTENT.advertisingLabel}</strong> — {MOCK_CONTENT.advertisingDesc}\n </li>\n </ul>\n <div\n className=\"consent:flex consent:flex-wrap consent:gap-2\"\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\" }}\n >\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.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 borderRadius: \"0.5rem\",\n border: \"1px solid #d1d5db\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#374151\",\n }}\n >\n Reject\n </button>\n <button\n type=\"button\"\n onClick={handleAccept}\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 }}\n >\n Accept\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 Something went wrong. Please try again.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","COOKIE_CONSENT_NAME","CookieConsentBanner","EntityStatus","MediaType","PhygitalConsentProvider","consentQueryKeys","consentService","getConsentPreferenceCookie","getCookie","setConsentPreferenceCookie","setCookie","useConsentById","useCreateDeviceCookieConsent","useCreateUserConsent","useHealth","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useManyConsents","usePhygitalConsent","__toCommonJS","MediaType","EntityStatus","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","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","import_jsx_runtime","PhygitalConsentContext","PhygitalConsentProvider","props","children","consentServiceProps","hasConsentPreference","setHasConsentPreference","refreshConsentPreference","stored","getConsentPreferenceCookie","value","ConsentServiceProvider","usePhygitalConsent","context","CONSENT_V1","consentService","axiosInstance","data","params","id","user_id","body","device_id","import_react_query","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useManyConsents","useConsentById","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","body","_data","variables","useCreateDeviceCookieConsent","import_react","import_jsx_runtime","PREFERENCES","MOCK_CONTENT","CookieConsentBanner","deviceId","onSubmitted","onDismiss","className","dismissed","setDismissed","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","handleReject","handleAccept"]}
|
|
1
|
+
{"version":3,"sources":["../index.ts","../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/helpers/cookie.ts","../src/provider/ConsentServiceProvider.tsx","../src/api/consent.ts","../src/hooks/useConsent.ts","../src/helpers/sha256.ts","../src/components/CookieConsentBanner.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 { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n /** True when cookie phygital_cookie_consent exists and has valid deviceId + selected_preferences. Use to show/hide cookie banner. */\n hasConsentPreference: boolean;\n /** Re-read cookie and update hasConsentPreference. Call after storing preference (e.g. from CookieConsentBanner onSuccess). */\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n children: React.ReactNode;\n}\n\n/**\n * Main provider for consumer apps. Wraps ConsentServiceProvider and exposes\n * hasConsentPreference + refreshConsentPreference so the app can show/hide\n * the cookie banner based on whether the user has already chosen a preference.\n */\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, ...consentServiceProps } = props;\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 hasConsentPreference,\n refreshConsentPreference,\n };\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentContext.Provider value={value}>\n {children}\n </PhygitalConsentContext.Provider>\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","/**\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","\"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 { AxiosInstance } from \"axios\";\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\nconst CONSENT_V1 = \"/consent/v1\";\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 `${CONSENT_V1}/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 `${CONSENT_V1}/consent/${id}`\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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/cookies/device-id`,\n body\n );\n return data;\n },\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","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n * Returns lowercase hex string.\n */\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const buffer = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", buffer);\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 * UTF-8 input, hex output.\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;\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\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. Returns 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 { useState } from \"react\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\n\nconst PREFERENCES = [\"essential\", \"analytics\", \"advertising\"] as const;\ntype PreferenceKey = (typeof PREFERENCES)[number];\n\nconst PREFERENCE_CONFIG: Record<\n PreferenceKey,\n { label: string; description: string; disabled?: boolean }\n> = {\n essential: {\n label: \"Essential\",\n description: \"Required for the site to work (e.g. security, preferences).\",\n },\n analytics: {\n label: \"Analytics\",\n description: \"Help us improve by collecting anonymous usage data.\",\n },\n advertising: {\n label: \"Advertising\",\n description: \"Used to show you relevant ads and measure campaigns.\",\n },\n};\n\nconst MOCK_CONTENT = {\n title: \"Cookie preferences\",\n description:\n \"We use cookies to improve your experience. Choose which categories to enable, then save.\",\n};\n\nexport interface CookieConsentBannerProps {\n /** Device ID (consumer app must provide; will be hashed by app before API if required). */\n deviceId: string;\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 deviceId,\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [preferences, setPreferences] = useState<Record<PreferenceKey, boolean>>({\n essential: true,\n analytics: false,\n advertising: false,\n });\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n PREFERENCES.filter((key) => preferences[key]);\n\n const togglePreference = (key: PreferenceKey) => {\n if (PREFERENCE_CONFIG[key].disabled) return;\n setPreferences((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const handleReject = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"REJECTED\",\n selected_preferences: [\"essential\"],\n });\n };\n\n const handleSave = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate({\n device_id: deviceId,\n status: \"ACCEPTED\",\n selected_preferences: selected.length > 0 ? selected : [],\n });\n };\n\n if (dismissed) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-label={MOCK_CONTENT.title}\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 {MOCK_CONTENT.title}\n </h2>\n <p\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\" }}\n >\n {MOCK_CONTENT.description}\n </p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}>\n {PREFERENCES.map((key) => {\n const config = PREFERENCE_CONFIG[key];\n const checked = preferences[key];\n return (\n <div\n key={key}\n style={{\n display: \"flex\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n gap: \"0.75rem\",\n }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>\n <strong\n className=\"consent:text-sm consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", color: \"#111827\" }}\n >\n {config.label}\n </strong>\n <p\n className=\"consent:text-xs consent:text-gray-500\"\n style={{ fontSize: \"0.75rem\", color: \"#6b7280\", margin: \"0.25rem 0 0\" }}\n >\n {config.description}\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${config.label} cookies ${checked ? \"on\" : \"off\"}`}\n disabled={config.disabled}\n onClick={() => togglePreference(key)}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: config.disabled ? \"default\" : \"pointer\",\n backgroundColor: config.disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: config.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 );\n })}\n </div>\n <div\n className=\"consent:flex consent:flex-wrap consent:gap-2\"\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\" }}\n >\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.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 borderRadius: \"0.5rem\",\n border: \"1px solid #d1d5db\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#374151\",\n }}\n >\n Reject\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\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 }}\n >\n Accept\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 Something went wrong. Please try again.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","COOKIE_CONSENT_NAME","CookieConsentBanner","EntityStatus","MediaType","PhygitalConsentProvider","consentQueryKeys","consentService","getConsentPreferenceCookie","getCookie","setConsentPreferenceCookie","setCookie","sha256","useConsentById","useCreateDeviceCookieConsent","useCreateUserConsent","useHealth","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useManyConsents","usePhygitalConsent","__toCommonJS","MediaType","EntityStatus","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","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","import_jsx_runtime","PhygitalConsentContext","PhygitalConsentProvider","props","children","consentServiceProps","hasConsentPreference","setHasConsentPreference","refreshConsentPreference","stored","getConsentPreferenceCookie","value","ConsentServiceProvider","usePhygitalConsent","context","CONSENT_V1","consentService","axiosInstance","data","params","id","user_id","body","device_id","import_react_query","arrayBufferToHex","buffer","b","sha256WebCrypto","value","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","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","import_jsx_runtime","PREFERENCES","PREFERENCE_CONFIG","MOCK_CONTENT","CookieConsentBanner","deviceId","onSubmitted","onDismiss","className","dismissed","setDismissed","preferences","setPreferences","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","key","togglePreference","prev","handleReject","handleSave","selected","config","checked"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -219,9 +219,9 @@ declare const useHealth: (options?: UseQueryOptions<HealthResponse>) => _tanstac
|
|
|
219
219
|
declare const useManyConsents: (params?: GetManyConsentParams, options?: UseQueryOptions<GetManyConsentResponse>) => _tanstack_react_query.UseQueryResult<GetManyConsentResponse, Error>;
|
|
220
220
|
/** Get consent log by ID – GET /consent/v1/consent/{id} */
|
|
221
221
|
declare const useConsentById: (params: GetConsentByIdParams, options?: UseQueryOptions<GetConsentByIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
222
|
-
/** Get latest consent for a user – GET /consent/v1/user-id */
|
|
222
|
+
/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */
|
|
223
223
|
declare const useLatestConsentByUserId: (params: GetLatestConsentByUserIdParams, options?: UseQueryOptions<GetLatestConsentByUserIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
224
|
-
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id */
|
|
224
|
+
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */
|
|
225
225
|
declare const useLatestCookieConsentByDeviceId: (params: GetLatestCookieConsentByDeviceIdParams, options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
226
226
|
/** Create user consent (account register) – POST /consent/v1/user-id */
|
|
227
227
|
declare const useCreateUserConsent: (options?: UseMutationOptions<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>) => _tanstack_react_query.UseMutationResult<ConsentLogDetailDTO, Error, CreateUserConsentUpsertDTO, unknown>;
|
|
@@ -263,6 +263,19 @@ declare function getConsentPreferenceCookie(): ConsentPreferenceValue | null;
|
|
|
263
263
|
*/
|
|
264
264
|
declare function setConsentPreferenceCookie(deviceId: string, selected_preferences: string[], options?: Partial<SetCookieOptions>): void;
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Hash user_id or device_id with SHA-256 for the two POST consent hooks
|
|
268
|
+
* (createUserConsent, createDeviceCookieConsent).
|
|
269
|
+
* Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.
|
|
270
|
+
* Returns lowercase hex string.
|
|
271
|
+
*/
|
|
272
|
+
/**
|
|
273
|
+
* Hash a string with SHA-256. Returns lowercase hex string.
|
|
274
|
+
* Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.
|
|
275
|
+
* Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).
|
|
276
|
+
*/
|
|
277
|
+
declare function sha256(value: string): Promise<string>;
|
|
278
|
+
|
|
266
279
|
interface CookieConsentBannerProps {
|
|
267
280
|
/** Device ID (consumer app must provide; will be hashed by app before API if required). */
|
|
268
281
|
deviceId: string;
|
|
@@ -275,4 +288,4 @@ interface CookieConsentBannerProps {
|
|
|
275
288
|
}
|
|
276
289
|
declare function CookieConsentBanner({ deviceId, onSubmitted, onDismiss, className, }: CookieConsentBannerProps): react_jsx_runtime.JSX.Element | null;
|
|
277
290
|
|
|
278
|
-
export { COOKIE_CONSENT_NAME, type CommonModel, type ConsentLogDetailDTO, type ConsentPreferenceValue, type ConsentStatus, CookieConsentBanner, type CookieConsentBannerProps, type CreateDeviceCookieConsentResponse, type CreateDeviceCookieConsentUpsertDTO, type CreateUserConsentResponse, type CreateUserConsentUpsertDTO, type DateTimeNumber, EntityStatus, type GetConsentByIdParams, type GetConsentByIdResponse, type GetLatestConsentByUserIdParams, type GetLatestConsentByUserIdResponse, type GetLatestCookieConsentByDeviceIdParams, type GetLatestCookieConsentByDeviceIdResponse, type GetManyConsentParams, type GetManyConsentResponse, type GetManyResponse, type GetOneResponse, type HealthResponse, type Media, MediaType, type PaginationRequest, type PaginationResponse, type PhygitalConsentContextValue, PhygitalConsentProvider, type PhygitalConsentProviderProps, type PolicyDetailDTO, type PolicyDocument, type PolicyPreference, type PolicyStatus, type PolicyType, type SetCookieOptions, consentQueryKeys, consentService, getConsentPreferenceCookie, getCookie, setConsentPreferenceCookie, setCookie, useConsentById, useCreateDeviceCookieConsent, useCreateUserConsent, useHealth, useLatestConsentByUserId, useLatestCookieConsentByDeviceId, useManyConsents, usePhygitalConsent };
|
|
291
|
+
export { COOKIE_CONSENT_NAME, type CommonModel, type ConsentLogDetailDTO, type ConsentPreferenceValue, type ConsentStatus, CookieConsentBanner, type CookieConsentBannerProps, type CreateDeviceCookieConsentResponse, type CreateDeviceCookieConsentUpsertDTO, type CreateUserConsentResponse, type CreateUserConsentUpsertDTO, type DateTimeNumber, EntityStatus, type GetConsentByIdParams, type GetConsentByIdResponse, type GetLatestConsentByUserIdParams, type GetLatestConsentByUserIdResponse, type GetLatestCookieConsentByDeviceIdParams, type GetLatestCookieConsentByDeviceIdResponse, type GetManyConsentParams, type GetManyConsentResponse, type GetManyResponse, type GetOneResponse, type HealthResponse, type Media, MediaType, type PaginationRequest, type PaginationResponse, type PhygitalConsentContextValue, PhygitalConsentProvider, type PhygitalConsentProviderProps, type PolicyDetailDTO, type PolicyDocument, type PolicyPreference, type PolicyStatus, type PolicyType, type SetCookieOptions, consentQueryKeys, consentService, getConsentPreferenceCookie, getCookie, setConsentPreferenceCookie, setCookie, sha256, useConsentById, useCreateDeviceCookieConsent, useCreateUserConsent, useHealth, useLatestConsentByUserId, useLatestCookieConsentByDeviceId, useManyConsents, usePhygitalConsent };
|
package/dist/index.d.ts
CHANGED
|
@@ -219,9 +219,9 @@ declare const useHealth: (options?: UseQueryOptions<HealthResponse>) => _tanstac
|
|
|
219
219
|
declare const useManyConsents: (params?: GetManyConsentParams, options?: UseQueryOptions<GetManyConsentResponse>) => _tanstack_react_query.UseQueryResult<GetManyConsentResponse, Error>;
|
|
220
220
|
/** Get consent log by ID – GET /consent/v1/consent/{id} */
|
|
221
221
|
declare const useConsentById: (params: GetConsentByIdParams, options?: UseQueryOptions<GetConsentByIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
222
|
-
/** Get latest consent for a user – GET /consent/v1/user-id */
|
|
222
|
+
/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */
|
|
223
223
|
declare const useLatestConsentByUserId: (params: GetLatestConsentByUserIdParams, options?: UseQueryOptions<GetLatestConsentByUserIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
224
|
-
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id */
|
|
224
|
+
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */
|
|
225
225
|
declare const useLatestCookieConsentByDeviceId: (params: GetLatestCookieConsentByDeviceIdParams, options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>) => _tanstack_react_query.UseQueryResult<ConsentLogDetailDTO, Error>;
|
|
226
226
|
/** Create user consent (account register) – POST /consent/v1/user-id */
|
|
227
227
|
declare const useCreateUserConsent: (options?: UseMutationOptions<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>) => _tanstack_react_query.UseMutationResult<ConsentLogDetailDTO, Error, CreateUserConsentUpsertDTO, unknown>;
|
|
@@ -263,6 +263,19 @@ declare function getConsentPreferenceCookie(): ConsentPreferenceValue | null;
|
|
|
263
263
|
*/
|
|
264
264
|
declare function setConsentPreferenceCookie(deviceId: string, selected_preferences: string[], options?: Partial<SetCookieOptions>): void;
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Hash user_id or device_id with SHA-256 for the two POST consent hooks
|
|
268
|
+
* (createUserConsent, createDeviceCookieConsent).
|
|
269
|
+
* Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.
|
|
270
|
+
* Returns lowercase hex string.
|
|
271
|
+
*/
|
|
272
|
+
/**
|
|
273
|
+
* Hash a string with SHA-256. Returns lowercase hex string.
|
|
274
|
+
* Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.
|
|
275
|
+
* Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).
|
|
276
|
+
*/
|
|
277
|
+
declare function sha256(value: string): Promise<string>;
|
|
278
|
+
|
|
266
279
|
interface CookieConsentBannerProps {
|
|
267
280
|
/** Device ID (consumer app must provide; will be hashed by app before API if required). */
|
|
268
281
|
deviceId: string;
|
|
@@ -275,4 +288,4 @@ interface CookieConsentBannerProps {
|
|
|
275
288
|
}
|
|
276
289
|
declare function CookieConsentBanner({ deviceId, onSubmitted, onDismiss, className, }: CookieConsentBannerProps): react_jsx_runtime.JSX.Element | null;
|
|
277
290
|
|
|
278
|
-
export { COOKIE_CONSENT_NAME, type CommonModel, type ConsentLogDetailDTO, type ConsentPreferenceValue, type ConsentStatus, CookieConsentBanner, type CookieConsentBannerProps, type CreateDeviceCookieConsentResponse, type CreateDeviceCookieConsentUpsertDTO, type CreateUserConsentResponse, type CreateUserConsentUpsertDTO, type DateTimeNumber, EntityStatus, type GetConsentByIdParams, type GetConsentByIdResponse, type GetLatestConsentByUserIdParams, type GetLatestConsentByUserIdResponse, type GetLatestCookieConsentByDeviceIdParams, type GetLatestCookieConsentByDeviceIdResponse, type GetManyConsentParams, type GetManyConsentResponse, type GetManyResponse, type GetOneResponse, type HealthResponse, type Media, MediaType, type PaginationRequest, type PaginationResponse, type PhygitalConsentContextValue, PhygitalConsentProvider, type PhygitalConsentProviderProps, type PolicyDetailDTO, type PolicyDocument, type PolicyPreference, type PolicyStatus, type PolicyType, type SetCookieOptions, consentQueryKeys, consentService, getConsentPreferenceCookie, getCookie, setConsentPreferenceCookie, setCookie, useConsentById, useCreateDeviceCookieConsent, useCreateUserConsent, useHealth, useLatestConsentByUserId, useLatestCookieConsentByDeviceId, useManyConsents, usePhygitalConsent };
|
|
291
|
+
export { COOKIE_CONSENT_NAME, type CommonModel, type ConsentLogDetailDTO, type ConsentPreferenceValue, type ConsentStatus, CookieConsentBanner, type CookieConsentBannerProps, type CreateDeviceCookieConsentResponse, type CreateDeviceCookieConsentUpsertDTO, type CreateUserConsentResponse, type CreateUserConsentUpsertDTO, type DateTimeNumber, EntityStatus, type GetConsentByIdParams, type GetConsentByIdResponse, type GetLatestConsentByUserIdParams, type GetLatestConsentByUserIdResponse, type GetLatestCookieConsentByDeviceIdParams, type GetLatestCookieConsentByDeviceIdResponse, type GetManyConsentParams, type GetManyConsentResponse, type GetManyResponse, type GetOneResponse, type HealthResponse, type Media, MediaType, type PaginationRequest, type PaginationResponse, type PhygitalConsentContextValue, PhygitalConsentProvider, type PhygitalConsentProviderProps, type PolicyDetailDTO, type PolicyDocument, type PolicyPreference, type PolicyStatus, type PolicyType, type SetCookieOptions, consentQueryKeys, consentService, getConsentPreferenceCookie, getCookie, setConsentPreferenceCookie, setCookie, sha256, useConsentById, useCreateDeviceCookieConsent, useCreateUserConsent, useHealth, useLatestConsentByUserId, useLatestCookieConsentByDeviceId, useManyConsents, usePhygitalConsent };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var A=(n=>(n.IMAGE="image",n.VIDEO="video",n))(A||{}),G=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(G||{});import{createContext as Q,useCallback as H,useContext as F,useEffect as V,useState as $}from"react";var v="phygital_cookie_consent";function w(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function E(e,t,n={}){if(typeof document>"u")return;let{maxAge:s=31536e3,path:o="/",sameSite:r="Lax",secure:i=!1}=n,c=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${o}; max-age=${s}; SameSite=${r}`;i&&(c+="; Secure"),document.cookie=c}function x(){let e=w(v);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 R(e,t,n){E(v,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}import{QueryClient as L,QueryClientProvider as _}from"@tanstack/react-query";import q from"axios";import{createContext as N,useContext as M,useMemo as P}from"react";import{jsx as h}from"react/jsx-runtime";var b=N(void 0),T=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:s={}})=>{let o=q.create({baseURL:e,...t});return o.interceptors.request.use(n.onFulfilled,n.onRejected),o.interceptors.response.use(s.onFulfilled,s.onRejected),{consentApi:o,updateHeaders:i=>{Object.entries(i).forEach(([c,m])=>{o.defaults.headers.common[c]=m})}}},k=({children:e,baseURL:t="",axiosConfig:n={},queryClient:s,queryClientConfig:o={},requestInterceptors:r={},responseInterceptors:i={}})=>{let c=P(()=>s||new L({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...o?.defaultOptions?.queries},mutations:{...o?.defaultOptions?.mutations}},queryCache:o?.queryCache,mutationCache:o?.mutationCache}),[s]),m=P(()=>{let g=T({baseURL:t,axiosConfig:n,requestInterceptors:r,responseInterceptors:i});return{consentApi:g.consentApi,updateHeaders:g.updateHeaders,queryClient:c}},[t,c,n,r,i]);return h(b.Provider,{value:m,children:h(_,{client:c,children:e})})},C=()=>{let e=M(b);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};import{jsx as I}from"react/jsx-runtime";var D=Q(void 0);function fe(e){let{children:t,...n}=e,[s,o]=$(!1),r=H(()=>{let c=x();o(!!c)},[]);V(()=>{r()},[r]);let i={hasConsentPreference:s,refreshConsentPreference:r};return I(k,{...n,children:I(D.Provider,{value:i,children:t})})}function ge(){let e=F(D);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var l="/consent/v1",p=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${l}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${l}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${l}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${l}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${l}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${l}/cookies/device-id`,t);return n}});import{useQuery as f,useMutation as U,useQueryClient as S}from"@tanstack/react-query";var a={all:["consent"],health:()=>[...a.all,"health"],list:e=>[...a.all,"list",e],detail:e=>[...a.all,"detail",e],latestByUserId:e=>[...a.all,"user-id",e],latestCookieByDeviceId:e=>[...a.all,"cookies","device-id",e]},Oe=e=>{let{consentApi:t}=C(),n=p(t);return f({queryKey:a.health(),queryFn:()=>n.getHealth(),...e})},Ae=(e,t)=>{let{consentApi:n}=C(),s=p(n);return f({queryKey:a.list(e),queryFn:()=>s.getManyConsents(e),...t})},Ge=(e,t)=>{let{consentApi:n}=C(),s=p(n);return f({queryKey:a.detail(e.id),queryFn:()=>s.getConsentById(e),enabled:!!e.id,...t})},we=(e,t)=>{let{consentApi:n}=C(),s=p(n);return f({queryKey:a.latestByUserId(e.user_id),queryFn:()=>s.getLatestConsentByUserId(e),enabled:!!e.user_id,...t})},Ee=(e,t)=>{let{consentApi:n}=C(),s=p(n);return f({queryKey:a.latestCookieByDeviceId(e.device_id),queryFn:()=>s.getLatestCookieConsentByDeviceId(e),enabled:!!e.device_id,...t})},Le=e=>{let{consentApi:t}=C(),n=S(),s=p(t);return U({mutationFn:o=>s.createUserConsent(o),onSuccess:(o,r)=>{r.user_id&&n.invalidateQueries({queryKey:a.latestByUserId(r.user_id)}),n.invalidateQueries({queryKey:a.list()})},...e})},B=e=>{let{consentApi:t}=C(),n=S(),s=p(t);return U({mutationFn:o=>s.createDeviceCookieConsent(o),onSuccess:(o,r)=>{n.invalidateQueries({queryKey:a.latestCookieByDeviceId(r.device_id)}),n.invalidateQueries({queryKey:a.list()})},...e})};import{useState as K}from"react";import{jsx as u,jsxs as y}from"react/jsx-runtime";var z=["essential","analytics","advertising"],d={title:"Cookie preferences",description:"We use cookies to improve your experience, remember your settings, and understand how you use our site. You can accept all, or reject non-essential cookies.",essentialLabel:"Essential",essentialDesc:"Required for the site to work (e.g. security, preferences).",analyticsLabel:"Analytics",analyticsDesc:"Help us improve by collecting anonymous usage data.",advertisingLabel:"Advertising",advertisingDesc:"Used to show you relevant ads and measure campaigns."};function Ve({deviceId:e,onSubmitted:t,onDismiss:n,className:s=""}){let[o,r]=K(!1),i=B({onSuccess:(g,O)=>{R(e,O.selected_preferences??[]),t?.(),n?.(),r(!0)}}),c=()=>{i.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},m=()=>{i.mutate({device_id:e,status:"ACCEPTED",selected_preferences:[...z]})};return o?null:y("div",{role:"dialog","aria-label":d.title,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 ${s}`,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:[u("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:d.title}),u("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:d.description}),y("ul",{className:"consent:text-sm consent:text-gray-600 consent:space-y-2",style:{fontSize:"0.875rem",color:"#4b5563",margin:0,paddingLeft:"1.25rem"},children:[y("li",{children:[u("strong",{children:d.essentialLabel})," \u2014 ",d.essentialDesc]}),y("li",{children:[u("strong",{children:d.analyticsLabel})," \u2014 ",d.analyticsDesc]}),y("li",{children:[u("strong",{children:d.advertisingLabel})," \u2014 ",d.advertisingDesc]})]}),y("div",{className:"consent:flex consent:flex-wrap consent:gap-2",style:{display:"flex",flexWrap:"wrap",gap:"0.5rem"},children:[u("button",{type:"button",onClick:c,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:{borderRadius:"0.5rem",border:"1px solid #d1d5db",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#374151"},children:"Reject"}),u("button",{type:"button",onClick:m,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 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"},children:"Accept"})]}),i.isError&&u("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"Something went wrong. Please try again."})]})}export{v as COOKIE_CONSENT_NAME,Ve as CookieConsentBanner,G as EntityStatus,A as MediaType,fe as PhygitalConsentProvider,a as consentQueryKeys,p as consentService,x as getConsentPreferenceCookie,w as getCookie,R as setConsentPreferenceCookie,E as setCookie,Ge as useConsentById,B as useCreateDeviceCookieConsent,Le as useCreateUserConsent,Oe as useHealth,we as useLatestConsentByUserId,Ee as useLatestCookieConsentByDeviceId,Ae as useManyConsents,ge as usePhygitalConsent};
|
|
2
|
+
var j=(n=>(n.IMAGE="image",n.VIDEO="video",n))(j||{}),J=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(J||{});import{createContext as re,useCallback as ie,useContext as ae,useEffect as ce,useState as de}from"react";var B="phygital_cookie_consent";function Y(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function X(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:i="Lax",secure:l=!1}=n,c=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${i}`;l&&(c+="; Secure"),document.cookie=c}function O(){let e=Y(B);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 _(e,t,n){X(B,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}import{QueryClient as Z,QueryClientProvider as ee}from"@tanstack/react-query";import te from"axios";import{createContext as ne,useContext as oe,useMemo as E}from"react";import{jsx as G}from"react/jsx-runtime";var N=ne(void 0),se=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=te.create({baseURL:e,...t});return s.interceptors.request.use(n.onFulfilled,n.onRejected),s.interceptors.response.use(o.onFulfilled,o.onRejected),{consentApi:s,updateHeaders:l=>{Object.entries(l).forEach(([c,a])=>{s.defaults.headers.common[c]=a})}}},q=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:i={},responseInterceptors:l={}})=>{let c=E(()=>o||new Z({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),a=E(()=>{let r=se({baseURL:t,axiosConfig:n,requestInterceptors:i,responseInterceptors:l});return{consentApi:r.consentApi,updateHeaders:r.updateHeaders,queryClient:c}},[t,c,n,i,l]);return G(N.Provider,{value:a,children:G(ee,{client:c,children:e})})},x=()=>{let e=oe(N);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};import{jsx as L}from"react/jsx-runtime";var T=re(void 0);function Te(e){let{children:t,...n}=e,[o,s]=de(!1),i=ie(()=>{let c=O();s(!!c)},[]);ce(()=>{i()},[i]);let l={hasConsentPreference:o,refreshConsentPreference:i};return L(q,{...n,children:L(T.Provider,{value:l,children:t})})}function Me(){let e=ae(T);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var R="/consent/v1",b=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${R}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${R}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${R}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${R}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${R}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${R}/cookies/device-id`,t);return n}});import{useQuery as S,useMutation as M,useQueryClient as H}from"@tanstack/react-query";function le(e){return Array.from(new Uint8Array(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async function ue(e){let t=new TextEncoder().encode(e),n=await crypto.subtle.digest("SHA-256",t);return le(n)}function pe(e){let t=fe(e),n=Ce(),o=ye(),s=new Uint32Array(64),i=t.length>>6;for(let c=0;c<i;c++){let a=c<<6;for(let u=0;u<16;u++){let g=a+(u<<2);s[u]=t[g]<<24|t[g+1]<<16|t[g+2]<<8|t[g+3]}for(let u=16;u<64;u++){let g=C(s[u-15],7)^C(s[u-15],18)^s[u-15]>>>3,D=C(s[u-2],17)^C(s[u-2],19)^s[u-2]>>>10;s[u]=s[u-16]+g+s[u-7]+D>>>0}let r=o[0],v=o[1],h=o[2],k=o[3],d=o[4],p=o[5],m=o[6],U=o[7];for(let u=0;u<64;u++){let g=C(d,6)^C(d,11)^C(d,25),D=d&p^~d&m,A=U+g+D+n[u]+s[u]>>>0,V=C(r,2)^C(r,13)^C(r,22),W=r&v^r&h^v&h,z=V+W>>>0;U=m,m=p,p=d,d=k+A>>>0,k=h,h=v,v=r,r=A+z>>>0}o[0]=o[0]+r>>>0,o[1]=o[1]+v>>>0,o[2]=o[2]+h>>>0,o[3]=o[3]+k>>>0,o[4]=o[4]+d>>>0,o[5]=o[5]+p>>>0,o[6]=o[6]+m>>>0,o[7]=o[7]+U>>>0}let l="";for(let c=0;c<8;c++){let a=o[c];l+=(a>>>28).toString(16)+(a>>>24&15).toString(16)+(a>>>20&15).toString(16)+(a>>>16&15).toString(16)+(a>>>12&15).toString(16)+(a>>>8&15).toString(16)+(a>>>4&15).toString(16)+(a&15).toString(16)}return l}function C(e,t){return e>>>t|e<<32-t}function fe(e){let t=e.length,n=[];for(let a=0;a<t;a++){let r=e.charCodeAt(a);r<128?n.push(r):r<2048?n.push(192|r>>6,128|r&63):r<55296||r>=57344?n.push(224|r>>12,128|r>>6&63,128|r&63):(r=65536+((r&1023)<<10|e.charCodeAt(++a)&1023),n.push(240|r>>18,128|r>>12&63,128|r>>6&63,128|r&63))}let o=n.length,s=(64-(o+9)%64)%64,i=o+9+s,l=new Uint8Array(i);l.set(n),l[o]=128;let c=new DataView(l.buffer,l.byteOffset,l.byteLength);return c.setUint32(i-8,0,!1),c.setUint32(i-4,o*8>>>0,!1),l}function Ce(){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 ye(){return[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]}function me(){return!(typeof crypto>"u"||!crypto.subtle)}async function P(e){return typeof e!="string"||e.length===0?e:me()?ue(e):Promise.resolve(pe(e))}var f={all:["consent"],health:()=>[...f.all,"health"],list:e=>[...f.all,"list",e],detail:e=>[...f.all,"detail",e],latestByUserId:e=>[...f.all,"user-id",e],latestCookieByDeviceId:e=>[...f.all,"cookies","device-id",e]},tt=e=>{let{consentApi:t}=x(),n=b(t);return S({queryKey:f.health(),queryFn:()=>n.getHealth(),...e})},nt=(e,t)=>{let{consentApi:n}=x(),o=b(n);return S({queryKey:f.list(e),queryFn:()=>o.getManyConsents(e),...t})},ot=(e,t)=>{let{consentApi:n}=x(),o=b(n);return S({queryKey:f.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},st=(e,t)=>{let{consentApi:n}=x(),o=b(n);return S({queryKey:f.latestByUserId(e.user_id),queryFn:async()=>{let s=await P(e.user_id);return o.getLatestConsentByUserId({user_id:s})},enabled:!!e.user_id,...t})},rt=(e,t)=>{let{consentApi:n}=x(),o=b(n);return S({queryKey:f.latestCookieByDeviceId(e.device_id),queryFn:async()=>{let s=await P(e.device_id);return o.getLatestCookieConsentByDeviceId({device_id:s})},enabled:!!e.device_id,...t})},it=e=>{let{consentApi:t}=x(),n=H(),o=b(t);return M({mutationFn:async s=>{let i={...s};return s.user_id!=null&&s.user_id!==""&&(i.user_id=await P(s.user_id)),s.device_id!=null&&s.device_id!==""&&(i.device_id=await P(s.device_id)),o.createUserConsent(i)},onSuccess:(s,i)=>{i.user_id&&n.invalidateQueries({queryKey:f.latestByUserId(i.user_id)}),n.invalidateQueries({queryKey:f.list()})},...e})},Q=e=>{let{consentApi:t}=x(),n=H(),o=b(t);return M({mutationFn:async s=>{let i={...s,device_id:await P(s.device_id)};return o.createDeviceCookieConsent(i)},onSuccess:(s,i)=>{n.invalidateQueries({queryKey:f.latestCookieByDeviceId(i.device_id)}),n.invalidateQueries({queryKey:f.list()})},...e})};import{useState as F}from"react";import{jsx as y,jsxs as I}from"react/jsx-runtime";var K=["essential","analytics","advertising"],$={essential:{label:"Essential",description:"Required for the site to work (e.g. security, preferences)."},analytics:{label:"Analytics",description:"Help us improve by collecting anonymous usage data."},advertising:{label:"Advertising",description:"Used to show you relevant ads and measure campaigns."}},w={title:"Cookie preferences",description:"We use cookies to improve your experience. Choose which categories to enable, then save."};function mt({deviceId:e,onSubmitted:t,onDismiss:n,className:o=""}){let[s,i]=F(!1),[l,c]=F({essential:!0,analytics:!1,advertising:!1}),a=Q({onSuccess:(d,p)=>{_(e,p.selected_preferences??[]),t?.(),n?.(),i(!0)}}),r=()=>K.filter(d=>l[d]),v=d=>{$[d].disabled||c(p=>({...p,[d]:!p[d]}))},h=()=>{a.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},k=()=>{let d=r();a.mutate({device_id:e,status:"ACCEPTED",selected_preferences:d.length>0?d:[]})};return s?null:I("div",{role:"dialog","aria-label":w.title,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 ${o}`,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:[y("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:w.title}),y("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:w.description}),y("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:K.map(d=>{let p=$[d],m=l[d];return I("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"0.75rem"},children:[I("div",{style:{flex:1,minWidth:0},children:[y("strong",{className:"consent:text-sm consent:text-gray-900",style:{fontSize:"0.875rem",color:"#111827"},children:p.label}),y("p",{className:"consent:text-xs consent:text-gray-500",style:{fontSize:"0.75rem",color:"#6b7280",margin:"0.25rem 0 0"},children:p.description})]}),y("button",{type:"button",role:"switch","aria-checked":m,"aria-label":`${p.label} cookies ${m?"on":"off"}`,disabled:p.disabled,onClick:()=>v(d),style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:p.disabled?"default":"pointer",backgroundColor:p.disabled?"#e5e7eb":m?"#111827":"#d1d5db",opacity:p.disabled?.7:1,transition:"background-color 0.2s"},children:y("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:m?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]},d)})}),I("div",{className:"consent:flex consent:flex-wrap consent:gap-2",style:{display:"flex",flexWrap:"wrap",gap:"0.5rem"},children:[y("button",{type:"button",onClick:h,disabled:a.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:{borderRadius:"0.5rem",border:"1px solid #d1d5db",background:"#fff",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#374151"},children:"Reject"}),y("button",{type:"button",onClick:k,disabled:a.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"},children:"Accept"})]}),a.isError&&y("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"Something went wrong. Please try again."})]})}export{B as COOKIE_CONSENT_NAME,mt as CookieConsentBanner,J as EntityStatus,j as MediaType,Te as PhygitalConsentProvider,f as consentQueryKeys,b as consentService,O as getConsentPreferenceCookie,Y as getCookie,_ as setConsentPreferenceCookie,X as setCookie,P as sha256,ot as useConsentById,Q as useCreateDeviceCookieConsent,it as useCreateUserConsent,tt as useHealth,st as useLatestConsentByUserId,rt as useLatestCookieConsentByDeviceId,nt as useManyConsents,Me as usePhygitalConsent};
|
|
3
3
|
//# 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/helpers/cookie.ts","../src/provider/ConsentServiceProvider.tsx","../src/api/consent.ts","../src/hooks/useConsent.ts","../src/components/CookieConsentBanner.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 { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n /** True when cookie phygital_cookie_consent exists and has valid deviceId + selected_preferences. Use to show/hide cookie banner. */\n hasConsentPreference: boolean;\n /** Re-read cookie and update hasConsentPreference. Call after storing preference (e.g. from CookieConsentBanner onSuccess). */\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n children: React.ReactNode;\n}\n\n/**\n * Main provider for consumer apps. Wraps ConsentServiceProvider and exposes\n * hasConsentPreference + refreshConsentPreference so the app can show/hide\n * the cookie banner based on whether the user has already chosen a preference.\n */\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, ...consentServiceProps } = props;\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 hasConsentPreference,\n refreshConsentPreference,\n };\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentContext.Provider value={value}>\n {children}\n </PhygitalConsentContext.Provider>\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","/**\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","\"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 { AxiosInstance } from \"axios\";\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\nconst CONSENT_V1 = \"/consent/v1\";\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 `${CONSENT_V1}/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 `${CONSENT_V1}/consent/${id}`\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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/cookies/device-id`,\n body\n );\n return data;\n },\n };\n};\n","import {\n useQuery,\n useMutation,\n useQueryClient,\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\nimport { useConsentService } from \"../provider/ConsentServiceProvider\";\nimport { consentService } from \"../api/consent\";\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 */\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: () => service.getLatestConsentByUserId(params),\n enabled: !!params.user_id,\n ...options,\n });\n};\n\n/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id */\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: () => service.getLatestCookieConsentByDeviceId(params),\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: (body: CreateUserConsentUpsertDTO) => service.createUserConsent(body),\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: (body: CreateDeviceCookieConsentUpsertDTO) =>\n service.createDeviceCookieConsent(body),\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","\"use client\";\n\nimport { useState } from \"react\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\n\nconst PREFERENCES = [\"essential\", \"analytics\", \"advertising\"] as const;\n\nconst MOCK_CONTENT = {\n title: \"Cookie preferences\",\n description:\n \"We use cookies to improve your experience, remember your settings, and understand how you use our site. You can accept all, or reject non-essential cookies.\",\n essentialLabel: \"Essential\",\n essentialDesc: \"Required for the site to work (e.g. security, preferences).\",\n analyticsLabel: \"Analytics\",\n analyticsDesc: \"Help us improve by collecting anonymous usage data.\",\n advertisingLabel: \"Advertising\",\n advertisingDesc: \"Used to show you relevant ads and measure campaigns.\",\n};\n\nexport interface CookieConsentBannerProps {\n /** Device ID (consumer app must provide; will be hashed by app before API if required). */\n deviceId: string;\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 deviceId,\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n },\n });\n\n const handleReject = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"REJECTED\",\n selected_preferences: [\"essential\"],\n });\n };\n\n const handleAccept = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"ACCEPTED\",\n selected_preferences: [...PREFERENCES],\n });\n };\n\n if (dismissed) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-label={MOCK_CONTENT.title}\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 {MOCK_CONTENT.title}\n </h2>\n <p\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\" }}\n >\n {MOCK_CONTENT.description}\n </p>\n <ul\n className=\"consent:text-sm consent:text-gray-600 consent:space-y-2\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\", margin: 0, paddingLeft: \"1.25rem\" }}\n >\n <li>\n <strong>{MOCK_CONTENT.essentialLabel}</strong> — {MOCK_CONTENT.essentialDesc}\n </li>\n <li>\n <strong>{MOCK_CONTENT.analyticsLabel}</strong> — {MOCK_CONTENT.analyticsDesc}\n </li>\n <li>\n <strong>{MOCK_CONTENT.advertisingLabel}</strong> — {MOCK_CONTENT.advertisingDesc}\n </li>\n </ul>\n <div\n className=\"consent:flex consent:flex-wrap consent:gap-2\"\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\" }}\n >\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.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 borderRadius: \"0.5rem\",\n border: \"1px solid #d1d5db\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#374151\",\n }}\n >\n Reject\n </button>\n <button\n type=\"button\"\n onClick={handleAccept}\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 }}\n >\n Accept\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 Something went wrong. Please try again.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["MediaType","EntityStatus","createContext","useCallback","useContext","useEffect","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","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","jsx","PhygitalConsentContext","createContext","PhygitalConsentProvider","props","children","consentServiceProps","hasConsentPreference","setHasConsentPreference","useState","refreshConsentPreference","useCallback","stored","getConsentPreferenceCookie","useEffect","value","ConsentServiceProvider","usePhygitalConsent","context","useContext","CONSENT_V1","consentService","axiosInstance","data","params","id","user_id","body","device_id","useQuery","useMutation","useQueryClient","consentQueryKeys","params","id","user_id","device_id","useHealth","options","consentApi","useConsentService","service","consentService","useQuery","useManyConsents","useConsentById","useLatestConsentByUserId","useLatestCookieConsentByDeviceId","useCreateUserConsent","queryClient","useQueryClient","useMutation","body","_data","variables","useCreateDeviceCookieConsent","useState","jsx","jsxs","PREFERENCES","MOCK_CONTENT","CookieConsentBanner","deviceId","onSubmitted","onDismiss","className","dismissed","setDismissed","useState","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","handleReject","handleAccept"]}
|
|
1
|
+
{"version":3,"sources":["../src/types/common.ts","../src/provider/PhygitalConsentProvider.tsx","../src/helpers/cookie.ts","../src/provider/ConsentServiceProvider.tsx","../src/api/consent.ts","../src/hooks/useConsent.ts","../src/helpers/sha256.ts","../src/components/CookieConsentBanner.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 { getConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { ConsentServiceProvider, ConsentServiceProviderProps } from \"./ConsentServiceProvider\";\n\nexport interface PhygitalConsentContextValue {\n /** True when cookie phygital_cookie_consent exists and has valid deviceId + selected_preferences. Use to show/hide cookie banner. */\n hasConsentPreference: boolean;\n /** Re-read cookie and update hasConsentPreference. Call after storing preference (e.g. from CookieConsentBanner onSuccess). */\n refreshConsentPreference: () => void;\n}\n\nconst PhygitalConsentContext = createContext<PhygitalConsentContextValue | undefined>(undefined);\n\nexport interface PhygitalConsentProviderProps extends ConsentServiceProviderProps {\n children: React.ReactNode;\n}\n\n/**\n * Main provider for consumer apps. Wraps ConsentServiceProvider and exposes\n * hasConsentPreference + refreshConsentPreference so the app can show/hide\n * the cookie banner based on whether the user has already chosen a preference.\n */\nexport function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {\n const { children, ...consentServiceProps } = props;\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 hasConsentPreference,\n refreshConsentPreference,\n };\n\n return (\n <ConsentServiceProvider {...consentServiceProps}>\n <PhygitalConsentContext.Provider value={value}>\n {children}\n </PhygitalConsentContext.Provider>\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","/**\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","\"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 { AxiosInstance } from \"axios\";\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\nconst CONSENT_V1 = \"/consent/v1\";\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 `${CONSENT_V1}/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 `${CONSENT_V1}/consent/${id}`\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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/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 `${CONSENT_V1}/cookies/device-id`,\n body\n );\n return data;\n },\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","/**\n * Hash user_id or device_id with SHA-256 for the two POST consent hooks\n * (createUserConsent, createDeviceCookieConsent).\n * Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.\n * Returns lowercase hex string.\n */\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * SHA-256 via Web Crypto (modern browsers, Node 19+).\n */\nasync function sha256WebCrypto(value: string): Promise<string> {\n const buffer = new TextEncoder().encode(value);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", buffer);\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 * UTF-8 input, hex output.\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;\n}\n\nfunction rotr(n: number, b: number): number {\n return (n >>> b) | (n << (32 - b));\n}\n\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. Returns 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 { useState } from \"react\";\nimport { setConsentPreferenceCookie } from \"../helpers/cookie\";\nimport { useCreateDeviceCookieConsent } from \"../hooks/useConsent\";\n\nconst PREFERENCES = [\"essential\", \"analytics\", \"advertising\"] as const;\ntype PreferenceKey = (typeof PREFERENCES)[number];\n\nconst PREFERENCE_CONFIG: Record<\n PreferenceKey,\n { label: string; description: string; disabled?: boolean }\n> = {\n essential: {\n label: \"Essential\",\n description: \"Required for the site to work (e.g. security, preferences).\",\n },\n analytics: {\n label: \"Analytics\",\n description: \"Help us improve by collecting anonymous usage data.\",\n },\n advertising: {\n label: \"Advertising\",\n description: \"Used to show you relevant ads and measure campaigns.\",\n },\n};\n\nconst MOCK_CONTENT = {\n title: \"Cookie preferences\",\n description:\n \"We use cookies to improve your experience. Choose which categories to enable, then save.\",\n};\n\nexport interface CookieConsentBannerProps {\n /** Device ID (consumer app must provide; will be hashed by app before API if required). */\n deviceId: string;\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 deviceId,\n onSubmitted,\n onDismiss,\n className = \"\",\n}: CookieConsentBannerProps) {\n const [dismissed, setDismissed] = useState(false);\n const [preferences, setPreferences] = useState<Record<PreferenceKey, boolean>>({\n essential: true,\n analytics: false,\n advertising: false,\n });\n\n const createConsent = useCreateDeviceCookieConsent({\n onSuccess: (_data, variables) => {\n setConsentPreferenceCookie(deviceId, variables.selected_preferences ?? []);\n onSubmitted?.();\n onDismiss?.();\n setDismissed(true);\n },\n });\n\n const getSelectedPreferences = (): string[] =>\n PREFERENCES.filter((key) => preferences[key]);\n\n const togglePreference = (key: PreferenceKey) => {\n if (PREFERENCE_CONFIG[key].disabled) return;\n setPreferences((prev) => ({ ...prev, [key]: !prev[key] }));\n };\n\n const handleReject = () => {\n createConsent.mutate({\n device_id: deviceId,\n status: \"REJECTED\",\n selected_preferences: [\"essential\"],\n });\n };\n\n const handleSave = () => {\n const selected = getSelectedPreferences();\n createConsent.mutate({\n device_id: deviceId,\n status: \"ACCEPTED\",\n selected_preferences: selected.length > 0 ? selected : [],\n });\n };\n\n if (dismissed) return null;\n\n return (\n <div\n role=\"dialog\"\n aria-label={MOCK_CONTENT.title}\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 {MOCK_CONTENT.title}\n </h2>\n <p\n className=\"consent:text-sm consent:text-gray-600\"\n style={{ fontSize: \"0.875rem\", color: \"#4b5563\" }}\n >\n {MOCK_CONTENT.description}\n </p>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}>\n {PREFERENCES.map((key) => {\n const config = PREFERENCE_CONFIG[key];\n const checked = preferences[key];\n return (\n <div\n key={key}\n style={{\n display: \"flex\",\n alignItems: \"flex-start\",\n justifyContent: \"space-between\",\n gap: \"0.75rem\",\n }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>\n <strong\n className=\"consent:text-sm consent:text-gray-900\"\n style={{ fontSize: \"0.875rem\", color: \"#111827\" }}\n >\n {config.label}\n </strong>\n <p\n className=\"consent:text-xs consent:text-gray-500\"\n style={{ fontSize: \"0.75rem\", color: \"#6b7280\", margin: \"0.25rem 0 0\" }}\n >\n {config.description}\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={`${config.label} cookies ${checked ? \"on\" : \"off\"}`}\n disabled={config.disabled}\n onClick={() => togglePreference(key)}\n style={{\n flexShrink: 0,\n width: 44,\n height: 24,\n borderRadius: 12,\n border: \"none\",\n padding: 0,\n cursor: config.disabled ? \"default\" : \"pointer\",\n backgroundColor: config.disabled ? \"#e5e7eb\" : checked ? \"#111827\" : \"#d1d5db\",\n opacity: config.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 );\n })}\n </div>\n <div\n className=\"consent:flex consent:flex-wrap consent:gap-2\"\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\" }}\n >\n <button\n type=\"button\"\n onClick={handleReject}\n disabled={createConsent.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 borderRadius: \"0.5rem\",\n border: \"1px solid #d1d5db\",\n background: \"#fff\",\n padding: \"0.5rem 1rem\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: \"#374151\",\n }}\n >\n Reject\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\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 }}\n >\n Accept\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 Something went wrong. Please try again.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["MediaType","EntityStatus","createContext","useCallback","useContext","useEffect","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","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","jsx","PhygitalConsentContext","createContext","PhygitalConsentProvider","props","children","consentServiceProps","hasConsentPreference","setHasConsentPreference","useState","refreshConsentPreference","useCallback","stored","getConsentPreferenceCookie","useEffect","value","ConsentServiceProvider","usePhygitalConsent","context","useContext","CONSENT_V1","consentService","axiosInstance","data","params","id","user_id","body","device_id","useQuery","useMutation","useQueryClient","arrayBufferToHex","buffer","b","sha256WebCrypto","value","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","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","useState","jsx","jsxs","PREFERENCES","PREFERENCE_CONFIG","MOCK_CONTENT","CookieConsentBanner","deviceId","onSubmitted","onDismiss","className","dismissed","setDismissed","useState","preferences","setPreferences","createConsent","useCreateDeviceCookieConsent","_data","variables","setConsentPreferenceCookie","getSelectedPreferences","key","togglePreference","prev","handleReject","handleSave","selected","config","checked"]}
|
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
--consent-color-red-600: oklch(57.7% 0.245 27.325);
|
|
11
11
|
--consent-color-gray-200: oklch(92.8% 0.006 264.531);
|
|
12
12
|
--consent-color-gray-300: oklch(87.2% 0.01 258.338);
|
|
13
|
+
--consent-color-gray-500: oklch(55.1% 0.027 264.364);
|
|
13
14
|
--consent-color-gray-600: oklch(44.6% 0.03 256.802);
|
|
14
15
|
--consent-color-gray-700: oklch(37.3% 0.034 259.733);
|
|
15
16
|
--consent-color-gray-900: oklch(21% 0.034 264.665);
|
|
16
17
|
--consent-color-white: #fff;
|
|
17
18
|
--consent-spacing: 0.25rem;
|
|
18
19
|
--consent-container-md: 28rem;
|
|
20
|
+
--consent-text-xs: 0.75rem;
|
|
21
|
+
--consent-text-xs--line-height: calc(1 / 0.75);
|
|
19
22
|
--consent-text-sm: 0.875rem;
|
|
20
23
|
--consent-text-sm--line-height: calc(1.25 / 0.875);
|
|
21
24
|
--consent-text-lg: 1.125rem;
|
|
@@ -207,13 +210,6 @@
|
|
|
207
210
|
.consent\:gap-4 {
|
|
208
211
|
gap: calc(var(--consent-spacing) * 4);
|
|
209
212
|
}
|
|
210
|
-
.consent\:space-y-2 {
|
|
211
|
-
:where(& > :not(:last-child)) {
|
|
212
|
-
--tw-space-y-reverse: 0;
|
|
213
|
-
margin-block-start: calc(calc(var(--consent-spacing) * 2) * var(--tw-space-y-reverse));
|
|
214
|
-
margin-block-end: calc(calc(var(--consent-spacing) * 2) * calc(1 - var(--tw-space-y-reverse)));
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
213
|
.consent\:rounded-lg {
|
|
218
214
|
border-radius: var(--consent-radius-lg);
|
|
219
215
|
}
|
|
@@ -258,6 +254,10 @@
|
|
|
258
254
|
font-size: var(--consent-text-sm);
|
|
259
255
|
line-height: var(--tw-leading, var(--consent-text-sm--line-height));
|
|
260
256
|
}
|
|
257
|
+
.consent\:text-xs {
|
|
258
|
+
font-size: var(--consent-text-xs);
|
|
259
|
+
line-height: var(--tw-leading, var(--consent-text-xs--line-height));
|
|
260
|
+
}
|
|
261
261
|
.consent\:font-medium {
|
|
262
262
|
--tw-font-weight: var(--consent-font-weight-medium);
|
|
263
263
|
font-weight: var(--consent-font-weight-medium);
|
|
@@ -266,6 +266,9 @@
|
|
|
266
266
|
--tw-font-weight: var(--consent-font-weight-semibold);
|
|
267
267
|
font-weight: var(--consent-font-weight-semibold);
|
|
268
268
|
}
|
|
269
|
+
.consent\:text-gray-500 {
|
|
270
|
+
color: var(--consent-color-gray-500);
|
|
271
|
+
}
|
|
269
272
|
.consent\:text-gray-600 {
|
|
270
273
|
color: var(--consent-color-gray-600);
|
|
271
274
|
}
|
|
@@ -326,11 +329,6 @@
|
|
|
326
329
|
}
|
|
327
330
|
}
|
|
328
331
|
}
|
|
329
|
-
@property --tw-space-y-reverse {
|
|
330
|
-
syntax: "*";
|
|
331
|
-
inherits: false;
|
|
332
|
-
initial-value: 0;
|
|
333
|
-
}
|
|
334
332
|
@property --tw-border-style {
|
|
335
333
|
syntax: "*";
|
|
336
334
|
inherits: false;
|
|
@@ -408,7 +406,6 @@
|
|
|
408
406
|
@layer properties {
|
|
409
407
|
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
410
408
|
*, ::before, ::after, ::backdrop {
|
|
411
|
-
--tw-space-y-reverse: 0;
|
|
412
409
|
--tw-border-style: solid;
|
|
413
410
|
--tw-font-weight: initial;
|
|
414
411
|
--tw-shadow: 0 0 #0000;
|
package/package.json
CHANGED
|
@@ -5,17 +5,30 @@ import { setConsentPreferenceCookie } from "../helpers/cookie";
|
|
|
5
5
|
import { useCreateDeviceCookieConsent } from "../hooks/useConsent";
|
|
6
6
|
|
|
7
7
|
const PREFERENCES = ["essential", "analytics", "advertising"] as const;
|
|
8
|
+
type PreferenceKey = (typeof PREFERENCES)[number];
|
|
9
|
+
|
|
10
|
+
const PREFERENCE_CONFIG: Record<
|
|
11
|
+
PreferenceKey,
|
|
12
|
+
{ label: string; description: string; disabled?: boolean }
|
|
13
|
+
> = {
|
|
14
|
+
essential: {
|
|
15
|
+
label: "Essential",
|
|
16
|
+
description: "Required for the site to work (e.g. security, preferences).",
|
|
17
|
+
},
|
|
18
|
+
analytics: {
|
|
19
|
+
label: "Analytics",
|
|
20
|
+
description: "Help us improve by collecting anonymous usage data.",
|
|
21
|
+
},
|
|
22
|
+
advertising: {
|
|
23
|
+
label: "Advertising",
|
|
24
|
+
description: "Used to show you relevant ads and measure campaigns.",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
8
27
|
|
|
9
28
|
const MOCK_CONTENT = {
|
|
10
29
|
title: "Cookie preferences",
|
|
11
30
|
description:
|
|
12
|
-
"We use cookies to improve your experience
|
|
13
|
-
essentialLabel: "Essential",
|
|
14
|
-
essentialDesc: "Required for the site to work (e.g. security, preferences).",
|
|
15
|
-
analyticsLabel: "Analytics",
|
|
16
|
-
analyticsDesc: "Help us improve by collecting anonymous usage data.",
|
|
17
|
-
advertisingLabel: "Advertising",
|
|
18
|
-
advertisingDesc: "Used to show you relevant ads and measure campaigns.",
|
|
31
|
+
"We use cookies to improve your experience. Choose which categories to enable, then save.",
|
|
19
32
|
};
|
|
20
33
|
|
|
21
34
|
export interface CookieConsentBannerProps {
|
|
@@ -36,6 +49,11 @@ export function CookieConsentBanner({
|
|
|
36
49
|
className = "",
|
|
37
50
|
}: CookieConsentBannerProps) {
|
|
38
51
|
const [dismissed, setDismissed] = useState(false);
|
|
52
|
+
const [preferences, setPreferences] = useState<Record<PreferenceKey, boolean>>({
|
|
53
|
+
essential: true,
|
|
54
|
+
analytics: false,
|
|
55
|
+
advertising: false,
|
|
56
|
+
});
|
|
39
57
|
|
|
40
58
|
const createConsent = useCreateDeviceCookieConsent({
|
|
41
59
|
onSuccess: (_data, variables) => {
|
|
@@ -46,6 +64,14 @@ export function CookieConsentBanner({
|
|
|
46
64
|
},
|
|
47
65
|
});
|
|
48
66
|
|
|
67
|
+
const getSelectedPreferences = (): string[] =>
|
|
68
|
+
PREFERENCES.filter((key) => preferences[key]);
|
|
69
|
+
|
|
70
|
+
const togglePreference = (key: PreferenceKey) => {
|
|
71
|
+
if (PREFERENCE_CONFIG[key].disabled) return;
|
|
72
|
+
setPreferences((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
73
|
+
};
|
|
74
|
+
|
|
49
75
|
const handleReject = () => {
|
|
50
76
|
createConsent.mutate({
|
|
51
77
|
device_id: deviceId,
|
|
@@ -54,11 +80,12 @@ export function CookieConsentBanner({
|
|
|
54
80
|
});
|
|
55
81
|
};
|
|
56
82
|
|
|
57
|
-
const
|
|
83
|
+
const handleSave = () => {
|
|
84
|
+
const selected = getSelectedPreferences();
|
|
58
85
|
createConsent.mutate({
|
|
59
86
|
device_id: deviceId,
|
|
60
87
|
status: "ACCEPTED",
|
|
61
|
-
selected_preferences: [
|
|
88
|
+
selected_preferences: selected.length > 0 ? selected : [],
|
|
62
89
|
});
|
|
63
90
|
};
|
|
64
91
|
|
|
@@ -98,20 +125,72 @@ export function CookieConsentBanner({
|
|
|
98
125
|
>
|
|
99
126
|
{MOCK_CONTENT.description}
|
|
100
127
|
</p>
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
129
|
+
{PREFERENCES.map((key) => {
|
|
130
|
+
const config = PREFERENCE_CONFIG[key];
|
|
131
|
+
const checked = preferences[key];
|
|
132
|
+
return (
|
|
133
|
+
<div
|
|
134
|
+
key={key}
|
|
135
|
+
style={{
|
|
136
|
+
display: "flex",
|
|
137
|
+
alignItems: "flex-start",
|
|
138
|
+
justifyContent: "space-between",
|
|
139
|
+
gap: "0.75rem",
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
143
|
+
<strong
|
|
144
|
+
className="consent:text-sm consent:text-gray-900"
|
|
145
|
+
style={{ fontSize: "0.875rem", color: "#111827" }}
|
|
146
|
+
>
|
|
147
|
+
{config.label}
|
|
148
|
+
</strong>
|
|
149
|
+
<p
|
|
150
|
+
className="consent:text-xs consent:text-gray-500"
|
|
151
|
+
style={{ fontSize: "0.75rem", color: "#6b7280", margin: "0.25rem 0 0" }}
|
|
152
|
+
>
|
|
153
|
+
{config.description}
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
<button
|
|
157
|
+
type="button"
|
|
158
|
+
role="switch"
|
|
159
|
+
aria-checked={checked}
|
|
160
|
+
aria-label={`${config.label} cookies ${checked ? "on" : "off"}`}
|
|
161
|
+
disabled={config.disabled}
|
|
162
|
+
onClick={() => togglePreference(key)}
|
|
163
|
+
style={{
|
|
164
|
+
flexShrink: 0,
|
|
165
|
+
width: 44,
|
|
166
|
+
height: 24,
|
|
167
|
+
borderRadius: 12,
|
|
168
|
+
border: "none",
|
|
169
|
+
padding: 0,
|
|
170
|
+
cursor: config.disabled ? "default" : "pointer",
|
|
171
|
+
backgroundColor: config.disabled ? "#e5e7eb" : checked ? "#111827" : "#d1d5db",
|
|
172
|
+
opacity: config.disabled ? 0.7 : 1,
|
|
173
|
+
transition: "background-color 0.2s",
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<span
|
|
177
|
+
style={{
|
|
178
|
+
display: "block",
|
|
179
|
+
width: 20,
|
|
180
|
+
height: 20,
|
|
181
|
+
borderRadius: "50%",
|
|
182
|
+
backgroundColor: "#fff",
|
|
183
|
+
marginLeft: checked ? 22 : 2,
|
|
184
|
+
marginTop: 2,
|
|
185
|
+
transition: "margin-left 0.2s",
|
|
186
|
+
boxShadow: "0 1px 2px rgb(0 0 0 / 0.2)",
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
</button>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
})}
|
|
193
|
+
</div>
|
|
115
194
|
<div
|
|
116
195
|
className="consent:flex consent:flex-wrap consent:gap-2"
|
|
117
196
|
style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem" }}
|
|
@@ -135,7 +214,7 @@ export function CookieConsentBanner({
|
|
|
135
214
|
</button>
|
|
136
215
|
<button
|
|
137
216
|
type="button"
|
|
138
|
-
onClick={
|
|
217
|
+
onClick={handleSave}
|
|
139
218
|
disabled={createConsent.isPending}
|
|
140
219
|
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"
|
|
141
220
|
style={{
|
package/src/helpers/index.ts
CHANGED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash user_id or device_id with SHA-256 for the two POST consent hooks
|
|
3
|
+
* (createUserConsent, createDeviceCookieConsent).
|
|
4
|
+
* Uses Web Crypto when available; falls back to pure-JS SHA-256 for old devices.
|
|
5
|
+
* Returns lowercase hex string.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function arrayBufferToHex(buffer: ArrayBuffer): string {
|
|
9
|
+
return Array.from(new Uint8Array(buffer))
|
|
10
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
11
|
+
.join("");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* SHA-256 via Web Crypto (modern browsers, Node 19+).
|
|
16
|
+
*/
|
|
17
|
+
async function sha256WebCrypto(value: string): Promise<string> {
|
|
18
|
+
const buffer = new TextEncoder().encode(value);
|
|
19
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
20
|
+
return arrayBufferToHex(hashBuffer);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pure-JS SHA-256 fallback for environments without crypto.subtle
|
|
25
|
+
* (e.g. old browsers, insecure context, Node < 19).
|
|
26
|
+
* UTF-8 input, hex output.
|
|
27
|
+
*/
|
|
28
|
+
function sha256Fallback(value: string): string {
|
|
29
|
+
const bytes = utf8Encode(value);
|
|
30
|
+
const K = getK();
|
|
31
|
+
const H = getH();
|
|
32
|
+
const W = new Uint32Array(64);
|
|
33
|
+
const numBlocks = bytes.length >> 6;
|
|
34
|
+
|
|
35
|
+
for (let block = 0; block < numBlocks; block++) {
|
|
36
|
+
const start = block << 6;
|
|
37
|
+
for (let i = 0; i < 16; i++) {
|
|
38
|
+
const o = start + (i << 2);
|
|
39
|
+
W[i] =
|
|
40
|
+
(bytes[o]! << 24) |
|
|
41
|
+
(bytes[o + 1]! << 16) |
|
|
42
|
+
(bytes[o + 2]! << 8) |
|
|
43
|
+
bytes[o + 3]!;
|
|
44
|
+
}
|
|
45
|
+
for (let i = 16; i < 64; i++) {
|
|
46
|
+
const s0 = rotr(W[i - 15]!, 7) ^ rotr(W[i - 15]!, 18) ^ (W[i - 15]! >>> 3);
|
|
47
|
+
const s1 = rotr(W[i - 2]!, 17) ^ rotr(W[i - 2]!, 19) ^ (W[i - 2]! >>> 10);
|
|
48
|
+
W[i] = (W[i - 16]! + s0 + W[i - 7]! + s1) >>> 0;
|
|
49
|
+
}
|
|
50
|
+
let a = H[0]!,
|
|
51
|
+
b = H[1]!,
|
|
52
|
+
c = H[2]!,
|
|
53
|
+
d = H[3]!,
|
|
54
|
+
e = H[4]!,
|
|
55
|
+
f = H[5]!,
|
|
56
|
+
g = H[6]!,
|
|
57
|
+
h = H[7]!;
|
|
58
|
+
for (let i = 0; i < 64; i++) {
|
|
59
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
60
|
+
const ch = (e & f) ^ (~e & g);
|
|
61
|
+
const t1 = (h + S1 + ch + K[i]! + W[i]!) >>> 0;
|
|
62
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
63
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
64
|
+
const t2 = (S0 + maj) >>> 0;
|
|
65
|
+
h = g;
|
|
66
|
+
g = f;
|
|
67
|
+
f = e;
|
|
68
|
+
e = (d + t1) >>> 0;
|
|
69
|
+
d = c;
|
|
70
|
+
c = b;
|
|
71
|
+
b = a;
|
|
72
|
+
a = (t1 + t2) >>> 0;
|
|
73
|
+
}
|
|
74
|
+
H[0] = (H[0]! + a) >>> 0;
|
|
75
|
+
H[1] = (H[1]! + b) >>> 0;
|
|
76
|
+
H[2] = (H[2]! + c) >>> 0;
|
|
77
|
+
H[3] = (H[3]! + d) >>> 0;
|
|
78
|
+
H[4] = (H[4]! + e) >>> 0;
|
|
79
|
+
H[5] = (H[5]! + f) >>> 0;
|
|
80
|
+
H[6] = (H[6]! + g) >>> 0;
|
|
81
|
+
H[7] = (H[7]! + h) >>> 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let out = "";
|
|
85
|
+
for (let i = 0; i < 8; i++) {
|
|
86
|
+
const v = H[i]!;
|
|
87
|
+
out +=
|
|
88
|
+
(v >>> 28).toString(16) +
|
|
89
|
+
((v >>> 24) & 0xf).toString(16) +
|
|
90
|
+
((v >>> 20) & 0xf).toString(16) +
|
|
91
|
+
((v >>> 16) & 0xf).toString(16) +
|
|
92
|
+
((v >>> 12) & 0xf).toString(16) +
|
|
93
|
+
((v >>> 8) & 0xf).toString(16) +
|
|
94
|
+
((v >>> 4) & 0xf).toString(16) +
|
|
95
|
+
(v & 0xf).toString(16);
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function rotr(n: number, b: number): number {
|
|
101
|
+
return (n >>> b) | (n << (32 - b));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function utf8Encode(s: string): Uint8Array {
|
|
105
|
+
const n = s.length;
|
|
106
|
+
const bytes: number[] = [];
|
|
107
|
+
for (let i = 0; i < n; i++) {
|
|
108
|
+
let c = s.charCodeAt(i);
|
|
109
|
+
if (c < 0x80) {
|
|
110
|
+
bytes.push(c);
|
|
111
|
+
} else if (c < 0x800) {
|
|
112
|
+
bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
|
|
113
|
+
} else if (c < 0xd800 || c >= 0xe000) {
|
|
114
|
+
bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
|
|
115
|
+
} else {
|
|
116
|
+
c = 0x10000 + (((c & 0x3ff) << 10) | (s.charCodeAt(++i) & 0x3ff));
|
|
117
|
+
bytes.push(
|
|
118
|
+
0xf0 | (c >> 18),
|
|
119
|
+
0x80 | ((c >> 12) & 0x3f),
|
|
120
|
+
0x80 | ((c >> 6) & 0x3f),
|
|
121
|
+
0x80 | (c & 0x3f)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const len = bytes.length;
|
|
126
|
+
const pad = (64 - ((len + 9) % 64)) % 64;
|
|
127
|
+
const total = len + 9 + pad;
|
|
128
|
+
const out = new Uint8Array(total);
|
|
129
|
+
out.set(bytes);
|
|
130
|
+
out[len] = 0x80;
|
|
131
|
+
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
132
|
+
view.setUint32(total - 8, 0, false);
|
|
133
|
+
view.setUint32(total - 4, (len * 8) >>> 0, false);
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getK(): number[] {
|
|
138
|
+
const k: number[] = [];
|
|
139
|
+
const hex =
|
|
140
|
+
"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";
|
|
141
|
+
hex.split(" ").forEach((h) => k.push(parseInt(h, 16)));
|
|
142
|
+
return k;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getH(): number[] {
|
|
146
|
+
return [
|
|
147
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
148
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function hasWebCrypto(): boolean {
|
|
153
|
+
if (typeof crypto === "undefined" || !crypto.subtle) return false;
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Hash a string with SHA-256. Returns lowercase hex string.
|
|
159
|
+
* Use for user_id and device_id before calling createUserConsent / createDeviceCookieConsent.
|
|
160
|
+
* Uses Web Crypto when available; falls back to pure-JS for old devices (no crypto.subtle).
|
|
161
|
+
*/
|
|
162
|
+
export async function sha256(value: string): Promise<string> {
|
|
163
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
if (hasWebCrypto()) {
|
|
167
|
+
return sha256WebCrypto(value);
|
|
168
|
+
}
|
|
169
|
+
return Promise.resolve(sha256Fallback(value));
|
|
170
|
+
}
|
package/src/hooks/useConsent.ts
CHANGED
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
UseQueryOptions,
|
|
6
6
|
UseMutationOptions,
|
|
7
7
|
} from "@tanstack/react-query";
|
|
8
|
-
import { useConsentService } from "../provider/ConsentServiceProvider";
|
|
9
8
|
import { consentService } from "../api/consent";
|
|
9
|
+
import { sha256 } from "../helpers/sha256";
|
|
10
|
+
import { useConsentService } from "../provider/ConsentServiceProvider";
|
|
10
11
|
import type {
|
|
11
12
|
GetManyConsentParams,
|
|
12
13
|
GetManyConsentResponse,
|
|
@@ -83,7 +84,7 @@ export const useConsentById = (
|
|
|
83
84
|
});
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
/** Get latest consent for a user – GET /consent/v1/user-id */
|
|
87
|
+
/** Get latest consent for a user – GET /consent/v1/user-id (user_id hashed before send) */
|
|
87
88
|
export const useLatestConsentByUserId = (
|
|
88
89
|
params: GetLatestConsentByUserIdParams,
|
|
89
90
|
options?: UseQueryOptions<GetLatestConsentByUserIdResponse>
|
|
@@ -93,13 +94,16 @@ export const useLatestConsentByUserId = (
|
|
|
93
94
|
|
|
94
95
|
return useQuery<GetLatestConsentByUserIdResponse>({
|
|
95
96
|
queryKey: consentQueryKeys.latestByUserId(params.user_id),
|
|
96
|
-
queryFn: () =>
|
|
97
|
+
queryFn: async () => {
|
|
98
|
+
const user_id = await sha256(params.user_id);
|
|
99
|
+
return service.getLatestConsentByUserId({ user_id });
|
|
100
|
+
},
|
|
97
101
|
enabled: !!params.user_id,
|
|
98
102
|
...options,
|
|
99
103
|
});
|
|
100
104
|
};
|
|
101
105
|
|
|
102
|
-
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id */
|
|
106
|
+
/** Get latest cookie consent for a device – GET /consent/v1/cookies/device-id (device_id hashed before send) */
|
|
103
107
|
export const useLatestCookieConsentByDeviceId = (
|
|
104
108
|
params: GetLatestCookieConsentByDeviceIdParams,
|
|
105
109
|
options?: UseQueryOptions<GetLatestCookieConsentByDeviceIdResponse>
|
|
@@ -109,7 +113,10 @@ export const useLatestCookieConsentByDeviceId = (
|
|
|
109
113
|
|
|
110
114
|
return useQuery<GetLatestCookieConsentByDeviceIdResponse>({
|
|
111
115
|
queryKey: consentQueryKeys.latestCookieByDeviceId(params.device_id),
|
|
112
|
-
queryFn: () =>
|
|
116
|
+
queryFn: async () => {
|
|
117
|
+
const device_id = await sha256(params.device_id);
|
|
118
|
+
return service.getLatestCookieConsentByDeviceId({ device_id });
|
|
119
|
+
},
|
|
113
120
|
enabled: !!params.device_id,
|
|
114
121
|
...options,
|
|
115
122
|
});
|
|
@@ -130,7 +137,16 @@ export const useCreateUserConsent = (
|
|
|
130
137
|
const service = consentService(consentApi);
|
|
131
138
|
|
|
132
139
|
return useMutation<CreateUserConsentResponse, Error, CreateUserConsentUpsertDTO>({
|
|
133
|
-
mutationFn: (body: CreateUserConsentUpsertDTO) =>
|
|
140
|
+
mutationFn: async (body: CreateUserConsentUpsertDTO) => {
|
|
141
|
+
const payload = { ...body };
|
|
142
|
+
if (body.user_id != null && body.user_id !== "") {
|
|
143
|
+
payload.user_id = await sha256(body.user_id);
|
|
144
|
+
}
|
|
145
|
+
if (body.device_id != null && body.device_id !== "") {
|
|
146
|
+
payload.device_id = await sha256(body.device_id);
|
|
147
|
+
}
|
|
148
|
+
return service.createUserConsent(payload);
|
|
149
|
+
},
|
|
134
150
|
onSuccess: (
|
|
135
151
|
_data: CreateUserConsentResponse,
|
|
136
152
|
variables: CreateUserConsentUpsertDTO
|
|
@@ -163,8 +179,13 @@ export const useCreateDeviceCookieConsent = (
|
|
|
163
179
|
Error,
|
|
164
180
|
CreateDeviceCookieConsentUpsertDTO
|
|
165
181
|
>({
|
|
166
|
-
mutationFn: (body: CreateDeviceCookieConsentUpsertDTO) =>
|
|
167
|
-
|
|
182
|
+
mutationFn: async (body: CreateDeviceCookieConsentUpsertDTO) => {
|
|
183
|
+
const payload = {
|
|
184
|
+
...body,
|
|
185
|
+
device_id: await sha256(body.device_id),
|
|
186
|
+
};
|
|
187
|
+
return service.createDeviceCookieConsent(payload);
|
|
188
|
+
},
|
|
168
189
|
onSuccess: (
|
|
169
190
|
_data: CreateDeviceCookieConsentResponse,
|
|
170
191
|
variables: CreateDeviceCookieConsentUpsertDTO
|
|
@@ -34,7 +34,7 @@ export function PhygitalConsentProvider(props: PhygitalConsentProviderProps) {
|
|
|
34
34
|
useEffect(() => {
|
|
35
35
|
refreshConsentPreference();
|
|
36
36
|
}, [refreshConsentPreference]);
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
const value: PhygitalConsentContextValue = {
|
|
39
39
|
hasConsentPreference,
|
|
40
40
|
refreshConsentPreference,
|