@phygitallabs/phygital-consent 1.0.0 → 1.0.1

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 CHANGED
@@ -20,8 +20,8 @@
20
20
  "peerDependencies": {
21
21
  "@tanstack/react-query": "^5.66.8",
22
22
  "@tanstack/react-query-devtools": "^5.74.7",
23
- "react": ">=18.0.0",
24
- "react-dom": ">=18.0.0",
23
+ "react": ">=19.0.0",
24
+ "react-dom": ">=19.0.0",
25
25
  },
26
26
  },
27
27
  },
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 $=Object.create;var h=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var J=(e,t)=>{for(var n in t)h(e,n,{get:t[n],enumerable:!0})},E=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of z(t))!j.call(e,s)&&s!==n&&h(e,s,{get:()=>t[s],enumerable:!(o=V(t,s))||o.enumerable});return e};var Y=(e,t,n)=>(n=e!=null?$(W(e)):{},E(t||!e||!e.__esModule?h(n,"default",{value:e,enumerable:!0}):n,e)),X=e=>E(h({},"__esModule",{value:!0}),e);var de={};J(de,{COOKIE_CONSENT_NAME:()=>P,CookieConsentBanner:()=>ce,EntityStatus:()=>w,MediaType:()=>G,PhygitalConsentProvider:()=>ee,consentQueryKeys:()=>a,consentService:()=>y,getConsentPreferenceCookie:()=>k,getCookie:()=>A,setConsentPreferenceCookie:()=>I,setCookie:()=>N,useConsentById:()=>se,useCreateDeviceCookieConsent:()=>D,useCreateUserConsent:()=>ae,useHealth:()=>ne,useLatestConsentByUserId:()=>re,useLatestCookieConsentByDeviceId:()=>ie,useManyConsents:()=>oe,usePhygitalConsent:()=>te});module.exports=X(de);var G=(n=>(n.IMAGE="image",n.VIDEO="video",n))(G||{}),w=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(w||{});var u=require("react");var P="phygital_cookie_consent";function A(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function N(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:r="Lax",secure:p=!1}=n,d=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${r}`;p&&(d+="; Secure"),document.cookie=d}function k(){let e=A(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 I(e,t,n){N(P,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}var b=require("@tanstack/react-query"),_=Y(require("axios")),g=require("react"),S=require("react/jsx-runtime"),q=(0,g.createContext)(void 0),Z=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=_.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:p=>{Object.entries(p).forEach(([d,f])=>{s.defaults.headers.common[d]=f})}}},L=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:r={},responseInterceptors:p={}})=>{let d=(0,g.useMemo)(()=>o||new b.QueryClient({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),f=(0,g.useMemo)(()=>{let x=Z({baseURL:t,axiosConfig:n,requestInterceptors:r,responseInterceptors:p});return{consentApi:x.consentApi,updateHeaders:x.updateHeaders,queryClient:d}},[t,d,n,r,p]);return(0,S.jsx)(q.Provider,{value:f,children:(0,S.jsx)(b.QueryClientProvider,{client:d,children:e})})},m=()=>{let e=(0,g.useContext)(q);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};var U=require("react/jsx-runtime"),T=(0,u.createContext)(void 0);function ee(e){let{children:t,...n}=e,[o,s]=(0,u.useState)(!1),r=(0,u.useCallback)(()=>{let d=k();s(!!d)},[]);(0,u.useEffect)(()=>{r()},[r]);let p={hasConsentPreference:o,refreshConsentPreference:r};return(0,U.jsx)(L,{...n,children:(0,U.jsx)(T.Provider,{value:p,children:t})})}function te(){let e=(0,u.useContext)(T);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var v="/consent/v1",y=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${v}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${v}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${v}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${v}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${v}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${v}/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]},ne=e=>{let{consentApi:t}=m(),n=y(t);return(0,C.useQuery)({queryKey:a.health(),queryFn:()=>n.getHealth(),...e})},oe=(e,t)=>{let{consentApi:n}=m(),o=y(n);return(0,C.useQuery)({queryKey:a.list(e),queryFn:()=>o.getManyConsents(e),...t})},se=(e,t)=>{let{consentApi:n}=m(),o=y(n);return(0,C.useQuery)({queryKey:a.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},re=(e,t)=>{let{consentApi:n}=m(),o=y(n);return(0,C.useQuery)({queryKey:a.latestByUserId(e.user_id),queryFn:()=>o.getLatestConsentByUserId(e),enabled:!!e.user_id,...t})},ie=(e,t)=>{let{consentApi:n}=m(),o=y(n);return(0,C.useQuery)({queryKey:a.latestCookieByDeviceId(e.device_id),queryFn:()=>o.getLatestCookieConsentByDeviceId(e),enabled:!!e.device_id,...t})},ae=e=>{let{consentApi:t}=m(),n=(0,C.useQueryClient)(),o=y(t);return(0,C.useMutation)({mutationFn:s=>o.createUserConsent(s),onSuccess:(s,r)=>{r.user_id&&n.invalidateQueries({queryKey:a.latestByUserId(r.user_id)}),n.invalidateQueries({queryKey:a.list()})},...e})},D=e=>{let{consentApi:t}=m(),n=(0,C.useQueryClient)(),o=y(t);return(0,C.useMutation)({mutationFn:s=>o.createDeviceCookieConsent(s),onSuccess:(s,r)=>{n.invalidateQueries({queryKey:a.latestCookieByDeviceId(r.device_id)}),n.invalidateQueries({queryKey:a.list()})},...e})};var B=require("react");var i=require("react/jsx-runtime"),M=["essential","analytics","advertising"],Q={essential:{label:"Essential",description:"Required for the site to work (e.g. security, preferences).",disabled:!0},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."}},O={title:"Cookie preferences",description:"We use cookies to improve your experience. Choose which categories to enable, then save."};function ce({deviceId:e,onSubmitted:t,onDismiss:n,className:o=""}){let[s,r]=(0,B.useState)(!1),[p,d]=(0,B.useState)({essential:!0,analytics:!1,advertising:!1}),f=D({onSuccess:(c,l)=>{I(e,l.selected_preferences??[]),t?.(),n?.(),r(!0)}}),x=()=>M.filter(c=>p[c]),F=c=>{Q[c].disabled||d(l=>({...l,[c]:!l[c]}))},H=()=>{f.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},K=()=>{let c=x();f.mutate({device_id:e,status:"ACCEPTED",selected_preferences:c.length>0?c:[]})};return s?null:(0,i.jsxs)("div",{role:"dialog","aria-label":O.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,i.jsx)("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:O.title}),(0,i.jsx)("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:O.description}),(0,i.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:M.map(c=>{let l=Q[c],R=p[c];return(0,i.jsxs)("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"0.75rem"},children:[(0,i.jsxs)("div",{style:{flex:1,minWidth:0},children:[(0,i.jsx)("strong",{className:"consent:text-sm consent:text-gray-900",style:{fontSize:"0.875rem",color:"#111827"},children:l.label}),(0,i.jsx)("p",{className:"consent:text-xs consent:text-gray-500",style:{fontSize:"0.75rem",color:"#6b7280",margin:"0.25rem 0 0"},children:l.description})]}),(0,i.jsx)("button",{type:"button",role:"switch","aria-checked":R,"aria-label":`${l.label} cookies ${R?"on":"off"}`,disabled:l.disabled,onClick:()=>F(c),style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:l.disabled?"default":"pointer",backgroundColor:l.disabled?"#e5e7eb":R?"#111827":"#d1d5db",opacity:l.disabled?.7:1,transition:"background-color 0.2s"},children:(0,i.jsx)("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:R?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]},c)})}),(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:H,disabled:f.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 non-essential"}),(0,i.jsx)("button",{type:"button",onClick:K,disabled:f.isPending,className:"consent:rounded-lg consent:bg-gray-900 consent:px-4 consent:py-2 consent:text-sm consent:font-medium consent:text-white consent:shadow-sm hover:consent:bg-gray-800 disabled:consent:opacity-50",style:{borderRadius:"0.5rem",background:"#111827",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,color:"#fff"},children:"Save preferences"})]}),f.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});
3
3
  //# sourceMappingURL=index.cjs.map
@@ -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/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;\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 disabled: true,\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 non-essential\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 Save preferences\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","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.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 T=(n=>(n.IMAGE="image",n.VIDEO="video",n))(T||{}),M=(n=>(n.ACTIVE="Active",n.INACTIVE="Inactive",n))(M||{});import{createContext as j,useCallback as J,useContext as Y,useEffect as X,useState as Z}from"react";var h="phygital_cookie_consent";function Q(e){if(typeof document>"u")return null;let t=document.cookie.match(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)"));return t?decodeURIComponent(t[1]):null}function F(e,t,n={}){if(typeof document>"u")return;let{maxAge:o=31536e3,path:s="/",sameSite:r="Lax",secure:l=!1}=n,c=`${encodeURIComponent(e)}=${encodeURIComponent(t)}; path=${s}; max-age=${o}; SameSite=${r}`;l&&(c+="; Secure"),document.cookie=c}function b(){let e=Q(h);if(!e)return null;try{let t=JSON.parse(e);if(t&&typeof t=="object"&&"deviceId"in t&&"selected_preferences"in t&&typeof t.deviceId=="string"&&Array.isArray(t.selected_preferences))return t}catch{}return null}function P(e,t,n){F(h,JSON.stringify({deviceId:e,selected_preferences:t}),{maxAge:31536e3,path:"/",sameSite:"Lax",...n})}import{QueryClient as H,QueryClientProvider as K}from"@tanstack/react-query";import $ from"axios";import{createContext as V,useContext as z,useMemo as k}from"react";import{jsx as I}from"react/jsx-runtime";var S=V(void 0),W=({baseURL:e="",axiosConfig:t={},requestInterceptors:n={},responseInterceptors:o={}})=>{let s=$.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,p])=>{s.defaults.headers.common[c]=p})}}},U=({children:e,baseURL:t="",axiosConfig:n={},queryClient:o,queryClientConfig:s={},requestInterceptors:r={},responseInterceptors:l={}})=>{let c=k(()=>o||new H({defaultOptions:{queries:{refetchOnWindowFocus:!1,refetchOnMount:!1,refetchOnReconnect:!1,...s?.defaultOptions?.queries},mutations:{...s?.defaultOptions?.mutations}},queryCache:s?.queryCache,mutationCache:s?.mutationCache}),[o]),p=k(()=>{let g=W({baseURL:t,axiosConfig:n,requestInterceptors:r,responseInterceptors:l});return{consentApi:g.consentApi,updateHeaders:g.updateHeaders,queryClient:c}},[t,c,n,r,l]);return I(S.Provider,{value:p,children:I(K,{client:c,children:e})})},u=()=>{let e=z(S);if(!e)throw new Error("useConsentService must be used within a ConsentServiceProvider");return e};import{jsx as D}from"react/jsx-runtime";var O=j(void 0);function be(e){let{children:t,...n}=e,[o,s]=Z(!1),r=J(()=>{let c=b();s(!!c)},[]);X(()=>{r()},[r]);let l={hasConsentPreference:o,refreshConsentPreference:r};return D(U,{...n,children:D(O.Provider,{value:l,children:t})})}function Pe(){let e=Y(O);if(!e)throw new Error("usePhygitalConsent must be used within a PhygitalConsentProvider");return e}var y="/consent/v1",f=e=>({getHealth:async()=>{let{data:t}=await e.get("/health");return t},getManyConsents:async t=>{let{data:n}=await e.get(`${y}/consent`,{params:t?{type:t.type}:void 0});return n},getConsentById:async({id:t})=>{let{data:n}=await e.get(`${y}/consent/${t}`);return n},getLatestConsentByUserId:async({user_id:t})=>{let{data:n}=await e.get(`${y}/user-id`,{params:{user_id:t}});return n},createUserConsent:async t=>{let{data:n}=await e.post(`${y}/user-id`,t);return n},getLatestCookieConsentByDeviceId:async({device_id:t})=>{let{data:n}=await e.get(`${y}/cookies/device-id`,{params:{device_id:t}});return n},createDeviceCookieConsent:async t=>{let{data:n}=await e.post(`${y}/cookies/device-id`,t);return n}});import{useQuery as m,useMutation as B,useQueryClient as E}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]},_e=e=>{let{consentApi:t}=u(),n=f(t);return m({queryKey:a.health(),queryFn:()=>n.getHealth(),...e})},qe=(e,t)=>{let{consentApi:n}=u(),o=f(n);return m({queryKey:a.list(e),queryFn:()=>o.getManyConsents(e),...t})},Le=(e,t)=>{let{consentApi:n}=u(),o=f(n);return m({queryKey:a.detail(e.id),queryFn:()=>o.getConsentById(e),enabled:!!e.id,...t})},Te=(e,t)=>{let{consentApi:n}=u(),o=f(n);return m({queryKey:a.latestByUserId(e.user_id),queryFn:()=>o.getLatestConsentByUserId(e),enabled:!!e.user_id,...t})},Me=(e,t)=>{let{consentApi:n}=u(),o=f(n);return m({queryKey:a.latestCookieByDeviceId(e.device_id),queryFn:()=>o.getLatestCookieConsentByDeviceId(e),enabled:!!e.device_id,...t})},Qe=e=>{let{consentApi:t}=u(),n=E(),o=f(t);return B({mutationFn:s=>o.createUserConsent(s),onSuccess:(s,r)=>{r.user_id&&n.invalidateQueries({queryKey:a.latestByUserId(r.user_id)}),n.invalidateQueries({queryKey:a.list()})},...e})},G=e=>{let{consentApi:t}=u(),n=E(),o=f(t);return B({mutationFn:s=>o.createDeviceCookieConsent(s),onSuccess:(s,r)=>{n.invalidateQueries({queryKey:a.latestCookieByDeviceId(r.device_id)}),n.invalidateQueries({queryKey:a.list()})},...e})};import{useState as w}from"react";import{jsx as C,jsxs as x}from"react/jsx-runtime";var A=["essential","analytics","advertising"],N={essential:{label:"Essential",description:"Required for the site to work (e.g. security, preferences).",disabled:!0},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."}},R={title:"Cookie preferences",description:"We use cookies to improve your experience. Choose which categories to enable, then save."};function Je({deviceId:e,onSubmitted:t,onDismiss:n,className:o=""}){let[s,r]=w(!1),[l,c]=w({essential:!0,analytics:!1,advertising:!1}),p=G({onSuccess:(i,d)=>{P(e,d.selected_preferences??[]),t?.(),n?.(),r(!0)}}),g=()=>A.filter(i=>l[i]),_=i=>{N[i].disabled||c(d=>({...d,[i]:!d[i]}))},q=()=>{p.mutate({device_id:e,status:"REJECTED",selected_preferences:["essential"]})},L=()=>{let i=g();p.mutate({device_id:e,status:"ACCEPTED",selected_preferences:i.length>0?i:[]})};return s?null:x("div",{role:"dialog","aria-label":R.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:[C("h2",{className:"consent:text-lg consent:font-semibold consent:text-gray-900",style:{fontSize:"1.125rem",fontWeight:600,color:"#111827"},children:R.title}),C("p",{className:"consent:text-sm consent:text-gray-600",style:{fontSize:"0.875rem",color:"#4b5563"},children:R.description}),C("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:A.map(i=>{let d=N[i],v=l[i];return x("div",{style:{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:"0.75rem"},children:[x("div",{style:{flex:1,minWidth:0},children:[C("strong",{className:"consent:text-sm consent:text-gray-900",style:{fontSize:"0.875rem",color:"#111827"},children:d.label}),C("p",{className:"consent:text-xs consent:text-gray-500",style:{fontSize:"0.75rem",color:"#6b7280",margin:"0.25rem 0 0"},children:d.description})]}),C("button",{type:"button",role:"switch","aria-checked":v,"aria-label":`${d.label} cookies ${v?"on":"off"}`,disabled:d.disabled,onClick:()=>_(i),style:{flexShrink:0,width:44,height:24,borderRadius:12,border:"none",padding:0,cursor:d.disabled?"default":"pointer",backgroundColor:d.disabled?"#e5e7eb":v?"#111827":"#d1d5db",opacity:d.disabled?.7:1,transition:"background-color 0.2s"},children:C("span",{style:{display:"block",width:20,height:20,borderRadius:"50%",backgroundColor:"#fff",marginLeft:v?22:2,marginTop:2,transition:"margin-left 0.2s",boxShadow:"0 1px 2px rgb(0 0 0 / 0.2)"}})})]},i)})}),x("div",{className:"consent:flex consent:flex-wrap consent:gap-2",style:{display:"flex",flexWrap:"wrap",gap:"0.5rem"},children:[C("button",{type:"button",onClick:q,disabled:p.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 non-essential"}),C("button",{type:"button",onClick:L,disabled:p.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:"Save preferences"})]}),p.isError&&C("p",{className:"consent:text-sm consent:text-red-600",style:{fontSize:"0.875rem",color:"#dc2626"},children:"Something went wrong. Please try again."})]})}export{h as COOKIE_CONSENT_NAME,Je as CookieConsentBanner,M as EntityStatus,T as MediaType,be as PhygitalConsentProvider,a as consentQueryKeys,f as consentService,b as getConsentPreferenceCookie,Q as getCookie,P as setConsentPreferenceCookie,F as setCookie,Le as useConsentById,G as useCreateDeviceCookieConsent,Qe as useCreateUserConsent,_e as useHealth,Te as useLatestConsentByUserId,Me as useLatestCookieConsentByDeviceId,qe as useManyConsents,Pe 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/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;\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 disabled: true,\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 non-essential\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 Save preferences\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","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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phygitallabs/phygital-consent",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -5,17 +5,31 @@ 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
+ disabled: true,
18
+ },
19
+ analytics: {
20
+ label: "Analytics",
21
+ description: "Help us improve by collecting anonymous usage data.",
22
+ },
23
+ advertising: {
24
+ label: "Advertising",
25
+ description: "Used to show you relevant ads and measure campaigns.",
26
+ },
27
+ };
8
28
 
9
29
  const MOCK_CONTENT = {
10
30
  title: "Cookie preferences",
11
31
  description:
12
- "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.",
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.",
32
+ "We use cookies to improve your experience. Choose which categories to enable, then save.",
19
33
  };
20
34
 
21
35
  export interface CookieConsentBannerProps {
@@ -36,6 +50,11 @@ export function CookieConsentBanner({
36
50
  className = "",
37
51
  }: CookieConsentBannerProps) {
38
52
  const [dismissed, setDismissed] = useState(false);
53
+ const [preferences, setPreferences] = useState<Record<PreferenceKey, boolean>>({
54
+ essential: true,
55
+ analytics: false,
56
+ advertising: false,
57
+ });
39
58
 
40
59
  const createConsent = useCreateDeviceCookieConsent({
41
60
  onSuccess: (_data, variables) => {
@@ -46,6 +65,14 @@ export function CookieConsentBanner({
46
65
  },
47
66
  });
48
67
 
68
+ const getSelectedPreferences = (): string[] =>
69
+ PREFERENCES.filter((key) => preferences[key]);
70
+
71
+ const togglePreference = (key: PreferenceKey) => {
72
+ if (PREFERENCE_CONFIG[key].disabled) return;
73
+ setPreferences((prev) => ({ ...prev, [key]: !prev[key] }));
74
+ };
75
+
49
76
  const handleReject = () => {
50
77
  createConsent.mutate({
51
78
  device_id: deviceId,
@@ -54,11 +81,12 @@ export function CookieConsentBanner({
54
81
  });
55
82
  };
56
83
 
57
- const handleAccept = () => {
84
+ const handleSave = () => {
85
+ const selected = getSelectedPreferences();
58
86
  createConsent.mutate({
59
87
  device_id: deviceId,
60
88
  status: "ACCEPTED",
61
- selected_preferences: [...PREFERENCES],
89
+ selected_preferences: selected.length > 0 ? selected : [],
62
90
  });
63
91
  };
64
92
 
@@ -98,20 +126,72 @@ export function CookieConsentBanner({
98
126
  >
99
127
  {MOCK_CONTENT.description}
100
128
  </p>
101
- <ul
102
- className="consent:text-sm consent:text-gray-600 consent:space-y-2"
103
- style={{ fontSize: "0.875rem", color: "#4b5563", margin: 0, paddingLeft: "1.25rem" }}
104
- >
105
- <li>
106
- <strong>{MOCK_CONTENT.essentialLabel}</strong> — {MOCK_CONTENT.essentialDesc}
107
- </li>
108
- <li>
109
- <strong>{MOCK_CONTENT.analyticsLabel}</strong> — {MOCK_CONTENT.analyticsDesc}
110
- </li>
111
- <li>
112
- <strong>{MOCK_CONTENT.advertisingLabel}</strong> — {MOCK_CONTENT.advertisingDesc}
113
- </li>
114
- </ul>
129
+ <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
130
+ {PREFERENCES.map((key) => {
131
+ const config = PREFERENCE_CONFIG[key];
132
+ const checked = preferences[key];
133
+ return (
134
+ <div
135
+ key={key}
136
+ style={{
137
+ display: "flex",
138
+ alignItems: "flex-start",
139
+ justifyContent: "space-between",
140
+ gap: "0.75rem",
141
+ }}
142
+ >
143
+ <div style={{ flex: 1, minWidth: 0 }}>
144
+ <strong
145
+ className="consent:text-sm consent:text-gray-900"
146
+ style={{ fontSize: "0.875rem", color: "#111827" }}
147
+ >
148
+ {config.label}
149
+ </strong>
150
+ <p
151
+ className="consent:text-xs consent:text-gray-500"
152
+ style={{ fontSize: "0.75rem", color: "#6b7280", margin: "0.25rem 0 0" }}
153
+ >
154
+ {config.description}
155
+ </p>
156
+ </div>
157
+ <button
158
+ type="button"
159
+ role="switch"
160
+ aria-checked={checked}
161
+ aria-label={`${config.label} cookies ${checked ? "on" : "off"}`}
162
+ disabled={config.disabled}
163
+ onClick={() => togglePreference(key)}
164
+ style={{
165
+ flexShrink: 0,
166
+ width: 44,
167
+ height: 24,
168
+ borderRadius: 12,
169
+ border: "none",
170
+ padding: 0,
171
+ cursor: config.disabled ? "default" : "pointer",
172
+ backgroundColor: config.disabled ? "#e5e7eb" : checked ? "#111827" : "#d1d5db",
173
+ opacity: config.disabled ? 0.7 : 1,
174
+ transition: "background-color 0.2s",
175
+ }}
176
+ >
177
+ <span
178
+ style={{
179
+ display: "block",
180
+ width: 20,
181
+ height: 20,
182
+ borderRadius: "50%",
183
+ backgroundColor: "#fff",
184
+ marginLeft: checked ? 22 : 2,
185
+ marginTop: 2,
186
+ transition: "margin-left 0.2s",
187
+ boxShadow: "0 1px 2px rgb(0 0 0 / 0.2)",
188
+ }}
189
+ />
190
+ </button>
191
+ </div>
192
+ );
193
+ })}
194
+ </div>
115
195
  <div
116
196
  className="consent:flex consent:flex-wrap consent:gap-2"
117
197
  style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem" }}
@@ -131,11 +211,11 @@ export function CookieConsentBanner({
131
211
  color: "#374151",
132
212
  }}
133
213
  >
134
- Reject
214
+ Reject non-essential
135
215
  </button>
136
216
  <button
137
217
  type="button"
138
- onClick={handleAccept}
218
+ onClick={handleSave}
139
219
  disabled={createConsent.isPending}
140
220
  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
221
  style={{
@@ -147,7 +227,7 @@ export function CookieConsentBanner({
147
227
  color: "#fff",
148
228
  }}
149
229
  >
150
- Accept
230
+ Save preferences
151
231
  </button>
152
232
  </div>
153
233
  {createConsent.isError && (
@@ -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,