@tantainnovative/ndpr-toolkit 4.1.0 → 5.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +76 -12
  3. package/dist/chunk-4FXRJH37.js +1 -0
  4. package/dist/{chunk-UJGNW6CH.js → chunk-ASEUTU45.js} +1 -1
  5. package/dist/chunk-BRJKIF7E.mjs +1 -0
  6. package/dist/chunk-CSE36REY.js +6 -0
  7. package/dist/chunk-DKLJ5DYN.js +1 -0
  8. package/dist/chunk-FRMVSG4N.mjs +1 -0
  9. package/dist/chunk-MAD7QYRK.js +1 -0
  10. package/dist/chunk-NBOJ2KGN.mjs +1 -0
  11. package/dist/chunk-PQ5IPUJN.mjs +1 -0
  12. package/dist/chunk-QKXGVT2Q.js +1 -0
  13. package/dist/chunk-R2ZZMATR.js +1 -0
  14. package/dist/chunk-R3ZKV2J7.mjs +1 -0
  15. package/dist/chunk-RRVML7CU.mjs +1 -0
  16. package/dist/{chunk-6H6IXTHA.mjs → chunk-SBSYHCPK.mjs} +1 -1
  17. package/dist/chunk-TLIHFGIJ.js +1 -0
  18. package/dist/{chunk-O4ATGGVK.js → chunk-TVA6D6S4.js} +1 -1
  19. package/dist/chunk-WPH6CJDL.mjs +6 -0
  20. package/dist/{chunk-6OPGI27L.mjs → chunk-ZSRO4L3C.mjs} +1 -1
  21. package/dist/consent.d.mts +59 -21
  22. package/dist/consent.d.ts +59 -21
  23. package/dist/consent.js +1 -1
  24. package/dist/consent.mjs +1 -1
  25. package/dist/core.d.mts +116 -74
  26. package/dist/core.d.ts +116 -74
  27. package/dist/core.js +1 -1
  28. package/dist/core.mjs +1 -1
  29. package/dist/cross-border.d.mts +4 -27
  30. package/dist/cross-border.d.ts +4 -27
  31. package/dist/cross-border.js +1 -1
  32. package/dist/cross-border.mjs +1 -1
  33. package/dist/dsr.d.mts +86 -57
  34. package/dist/dsr.d.ts +86 -57
  35. package/dist/dsr.js +1 -1
  36. package/dist/dsr.mjs +1 -1
  37. package/dist/headless.d.mts +20 -27
  38. package/dist/headless.d.ts +20 -27
  39. package/dist/headless.js +1 -1
  40. package/dist/headless.mjs +1 -1
  41. package/dist/hooks.d.mts +20 -27
  42. package/dist/hooks.d.ts +20 -27
  43. package/dist/hooks.js +1 -1
  44. package/dist/hooks.mjs +1 -1
  45. package/dist/index.d.mts +37 -202
  46. package/dist/index.d.ts +37 -202
  47. package/dist/index.js +1 -1
  48. package/dist/index.mjs +1 -1
  49. package/dist/lawful-basis.d.mts +3 -24
  50. package/dist/lawful-basis.d.ts +3 -24
  51. package/dist/lawful-basis.js +1 -1
  52. package/dist/lawful-basis.mjs +1 -1
  53. package/dist/presets.d.mts +1 -9
  54. package/dist/presets.d.ts +1 -9
  55. package/dist/presets.js +1 -1
  56. package/dist/presets.mjs +1 -1
  57. package/dist/ropa-lite.d.mts +2 -11
  58. package/dist/ropa-lite.d.ts +2 -11
  59. package/dist/ropa-lite.js +1 -1
  60. package/dist/ropa-lite.mjs +1 -1
  61. package/dist/ropa.d.mts +10 -52
  62. package/dist/ropa.d.ts +10 -52
  63. package/dist/ropa.js +1 -1
  64. package/dist/ropa.mjs +1 -1
  65. package/dist/server.d.mts +20 -100
  66. package/dist/server.d.ts +20 -100
  67. package/dist/server.js +1 -1
  68. package/dist/server.mjs +1 -1
  69. package/package.json +1 -1
  70. package/dist/chunk-3APT25XO.mjs +0 -1
  71. package/dist/chunk-45D7AMB3.js +0 -1
  72. package/dist/chunk-65J4P5ID.js +0 -1
  73. package/dist/chunk-7Z7NURIA.mjs +0 -1
  74. package/dist/chunk-D3HHDWBR.js +0 -1
  75. package/dist/chunk-DSIIEUAD.mjs +0 -1
  76. package/dist/chunk-H2FDWK4F.js +0 -6
  77. package/dist/chunk-HLFS3NXG.js +0 -1
  78. package/dist/chunk-JGY65SHX.mjs +0 -1
  79. package/dist/chunk-NI54X543.mjs +0 -1
  80. package/dist/chunk-P5MPUC5F.js +0 -1
  81. package/dist/chunk-ROOUYQD4.js +0 -1
  82. package/dist/chunk-UTXDZDYF.mjs +0 -6
  83. package/dist/chunk-W6VVLHRQ.mjs +0 -1
@@ -0,0 +1,6 @@
1
+ import {e,g,h,f}from'./chunk-7BJXI2HI.mjs';import {a as a$1}from'./chunk-SFGW37LE.mjs';import {c}from'./chunk-FRQFU44F.mjs';import {b,a}from'./chunk-ZJYULEER.mjs';import {useState,useMemo,useEffect,useCallback}from'react';import {jsxs,jsx}from'react/jsx-runtime';var z=[{value:"adequacy_decision",label:"Adequacy Decision (Section 41)"},{value:"standard_clauses",label:"Standard Contractual Clauses (Section 42)"},{value:"binding_corporate_rules",label:"Binding Corporate Rules (Section 43)"},{value:"ndpc_authorization",label:"NDPC Authorization (Section 44)"},{value:"explicit_consent",label:"Explicit Consent (Section 45(a))"},{value:"contract_performance",label:"Contract Performance (Section 45(b))"},{value:"public_interest",label:"Public Interest (Section 45(c))"},{value:"legal_claims",label:"Legal Claims (Section 45(d))"},{value:"vital_interests",label:"Vital Interests (Section 45(e))"}],ae=[{value:"adequate",label:"Adequate"},{value:"inadequate",label:"Inadequate"},{value:"pending_review",label:"Pending Review"},{value:"unknown",label:"Unknown"}],ne=[{value:"one_time",label:"One-Time"},{value:"periodic",label:"Periodic"},{value:"continuous",label:"Continuous"}],ie=[{value:"active",label:"Active"},{value:"suspended",label:"Suspended"},{value:"terminated",label:"Terminated"},{value:"pending_approval",label:"Pending Approval"}],ye={low:"ndpr-badge ndpr-badge--success",medium:"ndpr-badge ndpr-badge--warning",high:"ndpr-badge ndpr-badge--destructive"},Se={active:"ndpr-badge ndpr-badge--success",suspended:"ndpr-badge ndpr-badge--warning",terminated:"ndpr-badge ndpr-badge--neutral",pending_approval:"ndpr-badge ndpr-badge--info"},ke={active:"Active",suspended:"Suspended",terminated:"Terminated",pending_approval:"Pending Approval"},de={destinationCountry:"",destinationCountryCode:"",adequacyStatus:"unknown",transferMechanism:"adequacy_decision",dataCategories:"",includesSensitiveData:false,estimatedDataSubjects:"",recipientOrganization:"",recipientContactName:"",recipientContactEmail:"",recipientContactPhone:"",recipientContactAddress:"",purpose:"",safeguards:"",riskAssessment:"",riskLevel:"medium",frequency:"one_time",status:"active",tiaCompleted:false,tiaReference:"",ndpcApprovalApplied:false,ndpcApprovalApproved:false,ndpcApprovalReference:""},Be=({transfers:g$1,onAdd:y,onUpdate:S,onArchive:k,summary:B,title:M,description:P,className:pe="",buttonClassName:I="",showSummary:oe=true,showTIA:le=true,classNames:r,unstyled:d})=>{var J,X;let $=c(),se=(J=M!=null?M:$.crossBorder.title)!=null?J:"Cross-Border Data Transfer Manager",ce=(X=P!=null?P:$.crossBorder.description)!=null?X:"Manage and document cross-border personal data transfers in compliance with NDPA 2023 Part VIII (Sections 41-43).",[f$1,T]=useState(null),[V,D]=useState(false),[h$1,U]=useState(null),[i,N]=useState(de),[C,G]=useState(null),[R,ue]=useState("all"),[q,me]=useState("all"),x=useMemo(()=>{let t=[...g$1];return R!=="all"&&(t=t.filter(a=>a.status===R)),q!=="all"&&(t=t.filter(a=>a.transferMechanism===q)),t.sort((a,o)=>o.updatedAt-a.updatedAt),t},[g$1,R,q]);useEffect(()=>{x.length>0&&!f$1&&T(x[0].id);},[x,f$1]);let H=useMemo(()=>{var t;return f$1&&(t=g$1.find(a=>a.id===f$1))!=null?t:null},[g$1,f$1]),A=useCallback(t=>new Date(t).toLocaleDateString(),[]),_=useCallback(()=>{N(de),U(null),G(null);},[]),ve=useCallback(()=>{_(),D(true);},[_]),fe=useCallback(t=>{var a,o,u,v;U(t.id),N({destinationCountry:t.destinationCountry,destinationCountryCode:t.destinationCountryCode||"",adequacyStatus:t.adequacyStatus,transferMechanism:t.transferMechanism,dataCategories:t.dataCategories.join(", "),includesSensitiveData:t.includesSensitiveData,estimatedDataSubjects:((a=t.estimatedDataSubjects)==null?void 0:a.toString())||"",recipientOrganization:t.recipientOrganization,recipientContactName:t.recipientContact.name,recipientContactEmail:t.recipientContact.email,recipientContactPhone:t.recipientContact.phone||"",recipientContactAddress:t.recipientContact.address||"",purpose:t.purpose,safeguards:t.safeguards.join(`
2
+ `),riskAssessment:t.riskAssessment,riskLevel:t.riskLevel,frequency:t.frequency,status:t.status,tiaCompleted:t.tiaCompleted,tiaReference:t.tiaReference||"",ndpcApprovalApplied:((o=t.ndpcApproval)==null?void 0:o.applied)||false,ndpcApprovalApproved:((u=t.ndpcApproval)==null?void 0:u.approved)||false,ndpcApprovalReference:((v=t.ndpcApproval)==null?void 0:v.referenceNumber)||""}),D(true);},[]),l=useCallback((t,a$1)=>{N(o=>b(a({},o),{[t]:a$1}));},[]),K=useCallback(()=>{let t=e(i.transferMechanism),a=Date.now();return {destinationCountry:i.destinationCountry.trim(),destinationCountryCode:i.destinationCountryCode.trim()||void 0,adequacyStatus:i.adequacyStatus,transferMechanism:i.transferMechanism,dataCategories:i.dataCategories.split(",").map(o=>o.trim()).filter(Boolean),includesSensitiveData:i.includesSensitiveData,estimatedDataSubjects:i.estimatedDataSubjects?parseInt(i.estimatedDataSubjects,10):void 0,recipientOrganization:i.recipientOrganization.trim(),recipientContact:{name:i.recipientContactName.trim(),email:i.recipientContactEmail.trim(),phone:i.recipientContactPhone.trim()||void 0,address:i.recipientContactAddress.trim()||void 0},purpose:i.purpose.trim(),safeguards:i.safeguards.split(`
3
+ `).map(o=>o.trim()).filter(Boolean),riskAssessment:i.riskAssessment.trim(),riskLevel:i.riskLevel,frequency:i.frequency,status:t&&!i.ndpcApprovalApproved?"pending_approval":i.status,tiaCompleted:i.tiaCompleted,tiaReference:i.tiaReference.trim()||void 0,ndpcApproval:t?{required:true,applied:i.ndpcApprovalApplied,approved:i.ndpcApprovalApproved||void 0,referenceNumber:i.ndpcApprovalReference.trim()||void 0,appliedAt:i.ndpcApprovalApplied?a:void 0,approvedAt:i.ndpcApprovalApproved?a:void 0}:void 0,startDate:a,reviewDate:a+365*24*60*60*1e3}},[i]),be=useCallback(()=>{let t=K(),a$1=a({id:h$1||"temp",createdAt:Date.now(),updatedAt:Date.now()},t),o=g(a$1);G(o),o.isValid&&(h$1?S==null||S(h$1,t):y==null||y(t),_(),D(false));},[K,h$1,S,y,_]),Y=useCallback(()=>{_(),D(false);},[_]),ge=useCallback(t=>{k==null||k(t),f$1===t&&T(null);},[k,f$1]),Q=useCallback(t=>jsxs("span",{className:a$1(`px-2 py-1 rounded text-xs font-medium ${ye[t]}`,r==null?void 0:r.riskBadge,d),children:[t.charAt(0).toUpperCase()+t.slice(1)," Risk"]}),[r==null?void 0:r.riskBadge,d]),W=useCallback(t=>jsx("span",{className:a$1(`px-2 py-1 rounded text-xs font-medium ${Se[t]}`,r==null?void 0:r.statusBadge,d),children:ke[t]}),[r==null?void 0:r.statusBadge,d]),he=useCallback(t=>{var u;e(t);let o=((u=z.find(v=>v.value===t))==null?void 0:u.label)||t;return jsx("span",{className:"px-2 py-1 rounded text-xs font-medium ndpr-badge ndpr-badge--info",children:o})},[]),w=useMemo(()=>{var v,c;if(B)return B;let t=0,a=[],o=[],u=[];for(let s of g$1){let O=s.status==="active";O&&t++,(s.status==="pending_approval"||(v=s.ndpcApproval)!=null&&v.required&&!((c=s.ndpcApproval)!=null&&c.approved))&&a.push(s),s.riskLevel==="high"&&O&&o.push(s),!s.tiaCompleted&&O&&u.push(s);}return {totalActiveTransfers:t,pendingApproval:a,highRiskTransfers:o,missingTIA:u,byMechanism:{},byAdequacy:{},dueForReview:[],lastUpdated:Date.now()}},[B,g$1]),Ce=()=>jsxs("div",{"data-ndpr-component":"cross-border-transfer-manager",role:"status","aria-label":"Transfer compliance summary",className:a$1("mb-6 grid grid-cols-2 md:grid-cols-4 gap-4",r==null?void 0:r.summary,d),children:[jsxs("div",{className:a$1("ndpr-alert ndpr-alert--info",r==null?void 0:r.summaryCard,d),children:[jsx("p",{className:"ndpr-stat__value ndpr-text-info",children:w.totalActiveTransfers}),jsx("p",{className:"text-sm ndpr-text-info",children:"Active Transfers"})]}),jsxs("div",{className:a$1("ndpr-alert ndpr-alert--info",r==null?void 0:r.summaryCard,d),children:[jsx("p",{className:"ndpr-stat__value ndpr-text-info",children:w.pendingApproval.length}),jsx("p",{className:"text-sm ndpr-text-info",children:"Pending Approval"})]}),jsxs("div",{className:a$1("ndpr-alert ndpr-alert--destructive",r==null?void 0:r.summaryCard,d),children:[jsx("p",{className:"ndpr-stat__value ndpr-text-destructive",children:w.highRiskTransfers.length}),jsx("p",{className:"text-sm ndpr-text-destructive",children:"High Risk"})]}),jsxs("div",{className:a$1("ndpr-alert ndpr-alert--warning",r==null?void 0:r.summaryCard,d),children:[jsx("p",{className:"ndpr-stat__value ndpr-text-warning",children:w.missingTIA.length}),jsx("p",{className:"text-sm ndpr-text-warning",children:"Missing TIA"})]})]}),_e=()=>{let t=e(i.transferMechanism);return jsxs("div",{className:a$1("bg-gray-50 dark:bg-gray-700 p-6 rounded-lg mb-6",r==null?void 0:r.form,d),children:[jsxs("div",{className:a$1("flex justify-between items-center mb-4",r==null?void 0:r.header,d),children:[jsx("h3",{className:"ndpr-section-heading",children:h$1?"Edit Transfer":"Add New Transfer"}),jsx("button",{onClick:Y,"aria-label":"Cancel form",className:"text-gray-600 hover:ndpr-text-muted dark:hover:text-gray-200",children:"Cancel"})]}),C&&!C.isValid&&jsxs("div",{id:"cb-form-errors",role:"alert",className:"ndpr-alert ndpr-alert--destructive",children:[jsx("p",{className:"text-sm font-medium ndpr-text-destructive mb-1",children:"Please fix the following errors:"}),jsx("ul",{className:"list-disc list-inside text-sm ndpr-text-destructive",children:C.errors.map((a,o)=>jsx("li",{children:a},o))})]}),C&&C.warnings.length>0&&jsxs("div",{id:"cb-form-warnings",role:"alert",className:"ndpr-alert ndpr-alert--warning",children:[jsx("p",{className:"text-sm font-medium ndpr-text-warning mb-1",children:"Warnings:"}),jsx("ul",{className:"list-disc list-inside text-sm ndpr-text-warning",children:C.warnings.map((a,o)=>jsx("li",{children:a},o))})]}),jsxs("div",{className:"ndpr-form-grid ndpr-form-grid--2",children:[jsxs("div",{children:[jsxs("label",{htmlFor:"cb-destinationCountry",className:"ndpr-form-field__label",children:["Destination Country ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("input",{id:"cb-destinationCountry",type:"text",value:i.destinationCountry,onChange:a=>l("destinationCountry",a.target.value),placeholder:"e.g. United Kingdom","aria-required":"true","aria-describedby":"cb-form-errors","aria-label":"Destination country",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-destinationCountryCode",className:"ndpr-form-field__label",children:"Country Code (ISO)"}),jsx("input",{id:"cb-destinationCountryCode",type:"text",value:i.destinationCountryCode,onChange:a=>l("destinationCountryCode",a.target.value),placeholder:"e.g. GB",maxLength:3,"aria-label":"Country code in ISO format",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsxs("label",{htmlFor:"cb-recipientOrganization",className:"ndpr-form-field__label",children:["Recipient Organization ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("input",{id:"cb-recipientOrganization",type:"text",value:i.recipientOrganization,onChange:a=>l("recipientOrganization",a.target.value),placeholder:"Organization name","aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsxs("label",{htmlFor:"cb-transferMechanism",className:"ndpr-form-field__label",children:["Transfer Mechanism ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("select",{id:"cb-transferMechanism",value:i.transferMechanism,onChange:a=>l("transferMechanism",a.target.value),"aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:z.map(a=>jsx("option",{value:a.value,children:a.label},a.value))})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-adequacyStatus",className:"ndpr-form-field__label",children:"Adequacy Status"}),jsx("select",{id:"cb-adequacyStatus",value:i.adequacyStatus,onChange:a=>l("adequacyStatus",a.target.value),"aria-label":"Select adequacy status of destination country",className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:ae.map(a=>jsx("option",{value:a.value,children:a.label},a.value))})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-riskLevel",className:"ndpr-form-field__label",children:"Risk Level"}),jsxs("select",{id:"cb-transfer-riskLevel",value:i.riskLevel,onChange:a=>l("riskLevel",a.target.value),className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:[jsx("option",{value:"low",children:"Low"}),jsx("option",{value:"medium",children:"Medium"}),jsx("option",{value:"high",children:"High"})]})]}),jsxs("div",{className:"md:col-span-2",children:[jsxs("label",{htmlFor:"cb-dataCategories",className:"ndpr-form-field__label",children:["Data Categories ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("input",{id:"cb-dataCategories",type:"text",value:i.dataCategories,onChange:a=>l("dataCategories",a.target.value),placeholder:"Comma-separated, e.g. Names, Email addresses, Phone numbers","aria-required":"true","aria-describedby":"cb-dataCategories-help",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)}),jsx("p",{id:"cb-dataCategories-help",className:"text-xs text-gray-500 mt-1",children:"Separate categories with commas"})]}),jsxs("div",{className:"flex items-center gap-2",children:[jsx("input",{type:"checkbox",id:"includesSensitiveData",checked:i.includesSensitiveData,onChange:a=>l("includesSensitiveData",a.target.checked),className:"ndpr-form-field__checkbox"}),jsx("label",{htmlFor:"includesSensitiveData",className:"ndpr-text-sm ndpr-font-medium",children:"Includes sensitive personal data"})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-estimatedDataSubjects",className:"ndpr-form-field__label",children:"Estimated Data Subjects"}),jsx("input",{id:"cb-transfer-estimatedDataSubjects",type:"number",value:i.estimatedDataSubjects,onChange:a=>l("estimatedDataSubjects",a.target.value),placeholder:"Number of data subjects",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsxs("label",{htmlFor:"cb-recipientContactName",className:"ndpr-form-field__label",children:["Recipient Contact Name ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("input",{id:"cb-recipientContactName",type:"text",value:i.recipientContactName,onChange:a=>l("recipientContactName",a.target.value),placeholder:"Contact person name","aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsxs("label",{htmlFor:"cb-recipientContactEmail",className:"ndpr-form-field__label",children:["Recipient Contact Email ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("input",{id:"cb-recipientContactEmail",type:"email",value:i.recipientContactEmail,onChange:a=>l("recipientContactEmail",a.target.value),placeholder:"contact@example.com","aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-recipientContactPhone",className:"ndpr-form-field__label",children:"Recipient Contact Phone"}),jsx("input",{id:"cb-transfer-recipientContactPhone",type:"text",value:i.recipientContactPhone,onChange:a=>l("recipientContactPhone",a.target.value),placeholder:"Phone number",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-recipientContactAddress",className:"ndpr-form-field__label",children:"Recipient Contact Address"}),jsx("input",{id:"cb-transfer-recipientContactAddress",type:"text",value:i.recipientContactAddress,onChange:a=>l("recipientContactAddress",a.target.value),placeholder:"Address",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{className:"md:col-span-2",children:[jsxs("label",{htmlFor:"cb-purpose",className:"ndpr-form-field__label",children:["Purpose of Transfer ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("textarea",{id:"cb-purpose",value:i.purpose,onChange:a=>l("purpose",a.target.value),placeholder:"Describe the purpose of this data transfer",rows:2,"aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{className:"md:col-span-2",children:[jsxs("label",{htmlFor:"cb-safeguards",className:"ndpr-form-field__label",children:["Safeguards ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("textarea",{id:"cb-safeguards",value:i.safeguards,onChange:a=>l("safeguards",a.target.value),placeholder:`One safeguard per line, e.g.
4
+ End-to-end encryption
5
+ Access control policies
6
+ Regular security audits`,rows:3,"aria-required":"true","aria-describedby":"cb-safeguards-help",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)}),jsx("p",{id:"cb-safeguards-help",className:"text-xs text-gray-500 mt-1",children:"Enter one safeguard per line"})]}),jsxs("div",{className:"md:col-span-2",children:[jsxs("label",{htmlFor:"cb-riskAssessment",className:"ndpr-form-field__label",children:["Risk Assessment Summary ",jsx("span",{className:"ndpr-form-field__required",children:"*"})]}),jsx("textarea",{id:"cb-riskAssessment",value:i.riskAssessment,onChange:a=>l("riskAssessment",a.target.value),placeholder:"Summarize the risk assessment for this transfer",rows:2,"aria-required":"true","aria-describedby":"cb-form-errors",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-frequency",className:"ndpr-form-field__label",children:"Transfer Frequency"}),jsx("select",{id:"cb-transfer-frequency",value:i.frequency,onChange:a=>l("frequency",a.target.value),className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:ne.map(a=>jsx("option",{value:a.value,children:a.label},a.value))})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-status",className:"ndpr-form-field__label",children:"Status"}),jsx("select",{id:"cb-transfer-status",value:i.status,onChange:a=>l("status",a.target.value),className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:ie.map(a=>jsx("option",{value:a.value,children:a.label},a.value))})]})]}),t&&jsxs("div",{className:"mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-md",children:[jsx("h4",{className:"text-sm font-medium ndpr-text-info mb-3",children:"NDPC Approval Required"}),jsx("p",{className:"text-xs ndpr-text-info mb-3",children:"The selected transfer mechanism requires approval from the Nigeria Data Protection Commission."}),jsxs("div",{className:"ndpr-form-grid ndpr-form-grid--2",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsx("input",{type:"checkbox",id:"ndpcApprovalApplied",checked:i.ndpcApprovalApplied,onChange:a=>l("ndpcApprovalApplied",a.target.checked),className:"ndpr-form-field__checkbox"}),jsx("label",{htmlFor:"ndpcApprovalApplied",className:"ndpr-text-sm",children:"Application submitted"})]}),jsxs("div",{className:"flex items-center gap-2",children:[jsx("input",{type:"checkbox",id:"ndpcApprovalApproved",checked:i.ndpcApprovalApproved,onChange:a=>l("ndpcApprovalApproved",a.target.checked),className:"ndpr-form-field__checkbox"}),jsx("label",{htmlFor:"ndpcApprovalApproved",className:"ndpr-text-sm",children:"Approval granted"})]}),jsxs("div",{className:"md:col-span-2",children:[jsx("label",{htmlFor:"cb-transfer-ndpcApprovalReference",className:"ndpr-form-field__label",children:"NDPC Reference Number"}),jsx("input",{id:"cb-transfer-ndpcApprovalReference",type:"text",value:i.ndpcApprovalReference,onChange:a=>l("ndpcApprovalReference",a.target.value),placeholder:"Reference number (if available)",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]})]})]}),le&&jsxs("div",{className:"mt-4 p-4 bg-gray-100 dark:bg-gray-600 rounded-md",children:[jsx("h4",{className:"text-sm font-medium mb-3",children:"Transfer Impact Assessment"}),jsxs("div",{className:"ndpr-form-grid ndpr-form-grid--2",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsx("input",{type:"checkbox",id:"tiaCompleted",checked:i.tiaCompleted,onChange:a=>l("tiaCompleted",a.target.checked),className:"ndpr-form-field__checkbox"}),jsx("label",{htmlFor:"tiaCompleted",className:"ndpr-text-sm",children:"TIA completed"})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cb-transfer-tiaReference",className:"ndpr-form-field__label",children:"TIA Reference"}),jsx("input",{id:"cb-transfer-tiaReference",type:"text",value:i.tiaReference,onChange:a=>l("tiaReference",a.target.value),placeholder:"Document reference",className:a$1("ndpr-form-field__input",r==null?void 0:r.input,d)})]})]})]}),jsxs("div",{className:"mt-4 flex justify-end gap-3",children:[jsx("button",{onClick:Y,className:"px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600",children:"Cancel"}),jsx("button",{onClick:be,className:a$1(`px-4 py-2 bg-[rgb(var(--ndpr-primary))] text-white rounded-md hover:bg-[rgb(var(--ndpr-primary-hover))] ${I}`,(r==null?void 0:r.primaryButton)||(r==null?void 0:r.submitButton),d),children:h$1?"Update Transfer":"Add Transfer"})]})]})},xe=t=>{var u,v;let a=h(t),o=g(t);return jsxs("div",{role:"status","aria-label":"Transfer details",className:a$1("",r==null?void 0:r.detailPanel,d),children:[jsxs("div",{className:"flex justify-between items-start mb-4",children:[jsxs("h3",{className:"ndpr-section-heading",children:[t.destinationCountry," \u2014 ",t.recipientOrganization]}),jsxs("div",{className:"flex gap-2",children:[W(t.status),Q(t.riskLevel)]})]}),!o.isValid&&jsxs("div",{className:"ndpr-alert ndpr-alert--destructive",children:[jsx("p",{className:"text-sm font-medium ndpr-text-destructive mb-1",children:"Compliance Issues"}),jsx("ul",{className:"list-disc list-inside text-sm ndpr-text-destructive",children:o.errors.map((c,s)=>jsx("li",{children:c},s))})]}),o.warnings.length>0&&jsxs("div",{className:"ndpr-alert ndpr-alert--warning",children:[jsx("p",{className:"text-sm font-medium ndpr-text-warning mb-1",children:"Warnings"}),jsx("ul",{className:"list-disc list-inside text-sm ndpr-text-warning",children:o.warnings.map((c,s)=>jsx("li",{children:c},s))})]}),jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mb-6",children:[jsxs("div",{children:[jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Destination:"})," ",t.destinationCountry,t.destinationCountryCode?` (${t.destinationCountryCode})`:""]}),jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Adequacy:"})," ",(u=ae.find(c=>c.value===t.adequacyStatus))==null?void 0:u.label]}),jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Frequency:"})," ",(v=ne.find(c=>c.value===t.frequency))==null?void 0:v.label]}),jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Start Date:"})," ",A(t.startDate)]}),t.endDate&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"End Date:"})," ",A(t.endDate)]}),t.reviewDate&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Next Review:"})," ",A(t.reviewDate)]})]}),jsxs("div",{children:[jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Recipient:"})," ",t.recipientOrganization]}),jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Contact:"})," ",t.recipientContact.name," (",t.recipientContact.email,")"]}),t.estimatedDataSubjects&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Data Subjects:"})," ",t.estimatedDataSubjects.toLocaleString()]}),jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Sensitive Data:"})," ",t.includesSensitiveData?"Yes":"No"]})]})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Transfer Mechanism"}),jsx("div",{className:"ndpr-panel",children:jsx("p",{className:"text-sm text-gray-700 dark:text-gray-200",children:f(t.transferMechanism)})})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Purpose"}),jsx("p",{className:"text-sm ndpr-text-muted bg-gray-50 dark:bg-gray-700 p-2 rounded-md",children:t.purpose})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Data Categories"}),jsx("div",{className:"flex flex-wrap gap-1",children:t.dataCategories.map((c,s)=>jsx("span",{className:"px-2 py-1 bg-gray-100 dark:bg-gray-600 rounded text-xs",children:c},s))})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Safeguards"}),jsx("ul",{className:"list-disc list-inside text-sm ndpr-text-muted",children:t.safeguards.map((c,s)=>jsx("li",{children:c},s))})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Risk Assessment"}),jsxs("div",{className:"ndpr-panel",children:[jsx("p",{className:"text-sm text-gray-700 dark:text-gray-200 mb-2",children:t.riskAssessment}),jsxs("div",{className:"mt-2",role:"status","aria-label":"Automated risk assessment result",children:[jsxs("p",{className:"text-xs font-medium ndpr-text-muted mb-1",children:["Automated Assessment (Score: ",a.riskScore,")"]}),a.factors.length>0&&jsx("ul",{className:"list-disc list-inside text-xs ndpr-text-muted",children:a.factors.map((c,s)=>jsx("li",{children:c},s))}),a.recommendations.length>0&&jsxs("div",{className:"mt-2",children:[jsx("p",{className:"text-xs font-medium ndpr-text-muted mb-1",children:"Recommendations:"}),jsx("ul",{className:"list-disc list-inside text-xs ndpr-text-muted",children:a.recommendations.map((c,s)=>jsx("li",{children:c},s))})]})]})]})]}),t.ndpcApproval&&jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"NDPC Approval"}),jsxs("div",{className:a$1(`p-3 rounded-md ${t.ndpcApproval.approved?"ndpr-alert ndpr-alert--success":t.ndpcApproval.applied?"ndpr-alert ndpr-alert--warning":"ndpr-alert ndpr-alert--destructive"}`,r==null?void 0:r.approvalStatus,d),children:[jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Status:"})," ",t.ndpcApproval.approved?"Approved":t.ndpcApproval.applied?"Application Submitted":"Not Applied"]}),t.ndpcApproval.referenceNumber&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Reference:"})," ",t.ndpcApproval.referenceNumber]}),t.ndpcApproval.appliedAt&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Applied:"})," ",A(t.ndpcApproval.appliedAt)]}),t.ndpcApproval.approvedAt&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Approved:"})," ",A(t.ndpcApproval.approvedAt)]})]})]}),jsxs("div",{className:"ndpr-form-field",children:[jsx("p",{className:"ndpr-form-field__label",children:"Transfer Impact Assessment"}),jsxs("div",{className:`p-3 rounded-md ${t.tiaCompleted?"ndpr-alert ndpr-alert--success":"ndpr-alert ndpr-alert--warning"}`,children:[jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Status:"})," ",t.tiaCompleted?"Completed":"Not Completed"]}),t.tiaReference&&jsxs("p",{className:"ndpr-text-sm",children:[jsx("span",{className:"font-medium",children:"Reference:"})," ",t.tiaReference]})]})]}),jsxs("div",{className:"flex gap-3 mt-6",children:[jsx("button",{onClick:()=>fe(t),"aria-label":`Edit transfer to ${t.destinationCountry}`,className:`px-4 py-2 bg-[rgb(var(--ndpr-primary))] text-white rounded-md hover:bg-[rgb(var(--ndpr-primary-hover))] ${I}`,children:"Edit"}),jsx("button",{onClick:()=>ge(t.id),"aria-label":`Remove transfer to ${t.destinationCountry}`,className:"px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700",children:"Remove"})]})]})};return jsxs("div",{className:a$1(`bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md ${pe}`,r==null?void 0:r.root,d),children:[jsx("h2",{className:a$1("ndpr-section-heading",r==null?void 0:r.title,d),children:se}),jsx("p",{className:"ndpr-card__subtitle",children:ce}),oe&&Ce(),!V&&jsx("div",{className:"mb-6",children:jsx("button",{onClick:ve,className:a$1(`px-4 py-2 bg-[rgb(var(--ndpr-primary))] text-white rounded-md hover:bg-[rgb(var(--ndpr-primary-hover))] ${I}`,(r==null?void 0:r.primaryButton)||(r==null?void 0:r.submitButton),d),children:"Add Transfer"})}),V&&_e(),jsxs("div",{className:"mb-6 grid grid-cols-1 md:grid-cols-2 gap-4",children:[jsxs("div",{children:[jsx("label",{htmlFor:"cbStatusFilter",className:"ndpr-form-field__label",children:"Status Filter"}),jsxs("select",{id:"cbStatusFilter",value:R,onChange:t=>ue(t.target.value),"aria-label":"Filter transfers by status",className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:[jsx("option",{value:"all",children:"All Statuses"}),ie.map(t=>jsx("option",{value:t.value,children:t.label},t.value))]})]}),jsxs("div",{children:[jsx("label",{htmlFor:"cbMechanismFilter",className:"ndpr-form-field__label",children:"Mechanism Filter"}),jsxs("select",{id:"cbMechanismFilter",value:q,onChange:t=>me(t.target.value),"aria-label":"Filter transfers by mechanism",className:a$1("ndpr-form-field__input",r==null?void 0:r.select,d),children:[jsx("option",{value:"all",children:"All Mechanisms"}),z.map(t=>jsx("option",{value:t.value,children:t.label},t.value))]})]})]}),jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-6",children:[jsxs("div",{className:"md:col-span-1",children:[jsx("h3",{className:"text-lg font-medium mb-3",children:"Transfers"}),x.length===0?jsx("p",{className:"ndpr-card__subtitle",children:"No cross-border transfers found."}):jsx("div",{className:a$1("space-y-2 max-h-96 overflow-y-auto pr-2",r==null?void 0:r.transferList,d),children:x.map(t=>{var a,o;return jsxs("div",{className:a$1(`p-3 rounded-md cursor-pointer ${f$1===t.id?"ndpr-alert ndpr-alert--info":"ndpr-panel"}`,r==null?void 0:r.transferItem,d),role:"button",tabIndex:0,"aria-label":`View transfer to ${t.destinationCountry}`,onClick:()=>T(t.id),onKeyDown:u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),T(t.id));},children:[jsxs("div",{className:"flex justify-between items-start mb-1",children:[jsx("h4",{className:"font-medium text-sm",children:t.destinationCountry}),Q(t.riskLevel)]}),jsx("p",{className:"text-xs ndpr-text-muted mb-1",children:t.recipientOrganization}),jsx("p",{className:"text-xs ndpr-text-muted mb-1",children:he(t.transferMechanism)}),jsxs("div",{className:"flex justify-between items-center mt-2",children:[W(t.status),((a=t.ndpcApproval)==null?void 0:a.required)&&!((o=t.ndpcApproval)!=null&&o.approved)&&jsx("span",{className:"text-xs ndpr-text-info",children:"NDPC Pending"})]})]},t.id)})})]}),jsx("div",{className:"md:col-span-2",children:H?xe(H):jsx("div",{className:"ndpr-empty-state",children:jsx("p",{className:"ndpr-card__subtitle",children:"Select a transfer to view details"})})})]})]})};export{Be as a};
@@ -1 +1 @@
1
- import {b as b$1}from'./chunk-3APT25XO.mjs';import {a as a$1}from'./chunk-DBZSN4WP.mjs';import {a,b}from'./chunk-ZJYULEER.mjs';import {useRef,useState,useEffect,useCallback}from'react';function K(f,R){return R?a$1(f):{load:()=>null,save:()=>{},remove:()=>{}}}function G({initialRequests:f=[],requestTypes:R,adapter:p,storageKey:T="ndpr_dsr_requests",useLocalStorage:h=false,onSubmit:g,onUpdate:m}){let y=p!=null?p:K(T,h),a$1=useRef(y);a$1.current=y;let[n,i]=useState(f),[k,l]=useState(true);useEffect(()=>{let e=false;try{let t=a$1.current.load();t instanceof Promise?t.then(s=>{e||(s&&i(s),l(!1));},()=>{e||l(!1);}):(t&&i(t),l(!1));}catch(t){e||l(false);}return ()=>{e=true;}},[]);let c=useCallback(e=>{Promise.resolve(a$1.current.save(e)).catch(t=>{console.warn("[ndpr-toolkit] Failed to save DSR requests:",t);});},[]),x=()=>"dsr_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),P=useCallback(e=>{let t=R.find(o=>o.id===e.type),s=(t==null?void 0:t.estimatedCompletionTime)||30,r=Date.now(),d=r+s*24*60*60*1e3,q=a({id:x(),status:"pending",createdAt:r,updatedAt:r,dueDate:d},e);return i(o=>{let S=[...o,q];return c(S),S}),g&&g(q),q},[R,c,g]),B=useCallback((e,t)=>{let s=null;return i(r=>{let d=r.findIndex(S=>S.id===e);if(d===-1)return r;let q=r[d];s=b(a(a({},q),t),{updatedAt:Date.now()});let o=[...r];return o[d]=s,c(o),o}),s&&m&&m(s),s},[c,m]),C=useCallback(e=>n.find(t=>t.id===e)||null,[n]),I=useCallback(e=>n.filter(t=>t.status===e),[n]),L=useCallback(e=>n.filter(t=>t.type===e),[n]),O=useCallback(e=>R.find(t=>t.id===e),[R]),_=useCallback(e=>{let{formattedRequest:t}=b$1(e);return t},[]),U=useCallback(()=>{i([]),Promise.resolve(a$1.current.remove()).catch(e=>{console.warn("[ndpr-toolkit] Failed to remove DSR requests:",e);});},[a$1]);return {requests:n,submitRequest:P,updateRequest:B,getRequest:C,getRequestsByStatus:I,getRequestsByType:L,getRequestType:O,formatRequest:_,clearRequests:U,isLoading:k}}export{G as a};
1
+ import {b as b$1}from'./chunk-RRVML7CU.mjs';import {a as a$1}from'./chunk-DBZSN4WP.mjs';import {a,b}from'./chunk-ZJYULEER.mjs';import {useRef,useState,useEffect,useCallback}from'react';function K(f,R){return R?a$1(f):{load:()=>null,save:()=>{},remove:()=>{}}}function G({initialRequests:f=[],requestTypes:R,adapter:p,storageKey:T="ndpr_dsr_requests",useLocalStorage:h=false,onSubmit:g,onUpdate:m}){let y=p!=null?p:K(T,h),a$1=useRef(y);a$1.current=y;let[n,i]=useState(f),[k,l]=useState(true);useEffect(()=>{let e=false;try{let t=a$1.current.load();t instanceof Promise?t.then(s=>{e||(s&&i(s),l(!1));},()=>{e||l(!1);}):(t&&i(t),l(!1));}catch(t){e||l(false);}return ()=>{e=true;}},[]);let c=useCallback(e=>{Promise.resolve(a$1.current.save(e)).catch(t=>{console.warn("[ndpr-toolkit] Failed to save DSR requests:",t);});},[]),x=()=>"dsr_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),P=useCallback(e=>{let t=R.find(o=>o.id===e.type),s=(t==null?void 0:t.estimatedCompletionTime)||30,r=Date.now(),d=r+s*24*60*60*1e3,q=a({id:x(),status:"pending",createdAt:r,updatedAt:r,dueDate:d},e);return i(o=>{let S=[...o,q];return c(S),S}),g&&g(q),q},[R,c,g]),B=useCallback((e,t)=>{let s=null;return i(r=>{let d=r.findIndex(S=>S.id===e);if(d===-1)return r;let q=r[d];s=b(a(a({},q),t),{updatedAt:Date.now()});let o=[...r];return o[d]=s,c(o),o}),s&&m&&m(s),s},[c,m]),C=useCallback(e=>n.find(t=>t.id===e)||null,[n]),I=useCallback(e=>n.filter(t=>t.status===e),[n]),L=useCallback(e=>n.filter(t=>t.type===e),[n]),O=useCallback(e=>R.find(t=>t.id===e),[R]),_=useCallback(e=>{let{formattedRequest:t}=b$1(e);return t},[]),U=useCallback(()=>{i([]),Promise.resolve(a$1.current.remove()).catch(e=>{console.warn("[ndpr-toolkit] Failed to remove DSR requests:",e);});},[a$1]);return {requests:n,submitRequest:P,updateRequest:B,getRequest:C,getRequestsByStatus:I,getRequestsByType:L,getRequestType:O,formatRequest:_,clearRequests:U,isLoading:k}}export{G as a};
@@ -213,7 +213,7 @@ declare interface ConsentContextValue {
213
213
  rejectAll: () => void;
214
214
  shouldShowBanner: boolean;
215
215
  isValid: boolean;
216
- validationErrors: string[];
216
+ validationErrors: StructuredValidationError[];
217
217
  resetConsent: () => void;
218
218
  isLoading: boolean;
219
219
  }
@@ -548,6 +548,30 @@ export declare interface StorageAdapter<T = unknown> {
548
548
  remove(): void | Promise<void>;
549
549
  }
550
550
 
551
+ /**
552
+ * Single structured validation error with a stable, locale-independent
553
+ * `code` consumers can switch on programmatically.
554
+ */
555
+ export declare interface StructuredValidationError {
556
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
557
+ field: string;
558
+ /** Stable, snake_case error code — safe to switch on across locales. */
559
+ code: string;
560
+ /** Human-readable English message — informational only; do not regex-match. */
561
+ message: string;
562
+ }
563
+
564
+ /**
565
+ * Result of a structured validator. `errors` is an array (one entry per
566
+ * failed rule). `data` is the narrowed, typed payload, only populated on
567
+ * `valid: true`.
568
+ */
569
+ export declare interface StructuredValidationResult<T> {
570
+ valid: boolean;
571
+ errors: StructuredValidationError[];
572
+ data?: T;
573
+ }
574
+
551
575
  /**
552
576
  * Hook for managing user consent in compliance with the NDPA.
553
577
  *
@@ -632,9 +656,10 @@ declare interface UseConsentReturn {
632
656
  */
633
657
  isValid: boolean;
634
658
  /**
635
- * Validation errors (if any)
659
+ * Validation errors (if any). Each entry is a structured `{ field, code,
660
+ * message }` so consumers can switch on `code` across locales.
636
661
  */
637
- validationErrors: string[];
662
+ validationErrors: StructuredValidationError[];
638
663
  /**
639
664
  * Reset consent settings (clear from storage)
640
665
  */
@@ -646,27 +671,40 @@ declare interface UseConsentReturn {
646
671
  }
647
672
 
648
673
  /**
649
- * Validates consent settings to ensure they meet NDPA requirements
650
- * @param settings The consent settings to validate
651
- * @returns An object containing validation result and any error messages
652
- * @deprecated Use `validateConsentStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
674
+ * Validates consent options against NDPA Section 26 requirements. Each option
675
+ * is checked for a non-empty `purpose`. Failing options are reported with
676
+ * `field: 'options[i].purpose'` so consumers can map errors back to the
677
+ * originating option index.
678
+ *
679
+ * Codes emitted:
680
+ * - `options_required` — empty / missing options array
681
+ * - `purpose_required` — single option missing a purpose
653
682
  */
654
- export declare function validateConsent(settings: ConsentSettings): {
655
- valid: boolean;
656
- errors: string[];
657
- };
683
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
658
684
 
659
685
  /**
660
- * Validates that consent options meet NDPA Section 26 requirements.
661
- * Each consent option must specify a purpose for which data will be processed,
662
- * as consent must be specific and informed per the Nigeria Data Protection Act.
663
- * @param options The consent options to validate
664
- * @returns An object containing validation result and any error messages
665
- * @deprecated Use `validateConsentOptionsStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
686
+ * Validates consent settings against NDPA requirements. Returns structured
687
+ * `{ field, code, message }[]` errors so consumers can switch on `code`
688
+ * across locales without regex-matching English strings.
689
+ *
690
+ * Codes emitted:
691
+ * - `consents_required`
692
+ * - `timestamp_required`
693
+ * - `timestamp_invalid`
694
+ * - `version_required`
695
+ * - `method_required`
696
+ * - `has_interacted_required`
697
+ * - `consent_stale`
698
+ *
699
+ * @example
700
+ * ```ts
701
+ * const { valid, errors, data } = validateConsentStructured(settings);
702
+ * if (!valid) {
703
+ * const stale = errors.find((e) => e.code === 'consent_stale');
704
+ * if (stale) showRefreshBanner();
705
+ * }
706
+ * ```
666
707
  */
667
- export declare function validateConsentOptions(options: ConsentOption[]): {
668
- valid: boolean;
669
- errors: string[];
670
- };
708
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
671
709
 
672
710
  export { }
package/dist/consent.d.ts CHANGED
@@ -213,7 +213,7 @@ declare interface ConsentContextValue {
213
213
  rejectAll: () => void;
214
214
  shouldShowBanner: boolean;
215
215
  isValid: boolean;
216
- validationErrors: string[];
216
+ validationErrors: StructuredValidationError[];
217
217
  resetConsent: () => void;
218
218
  isLoading: boolean;
219
219
  }
@@ -548,6 +548,30 @@ export declare interface StorageAdapter<T = unknown> {
548
548
  remove(): void | Promise<void>;
549
549
  }
550
550
 
551
+ /**
552
+ * Single structured validation error with a stable, locale-independent
553
+ * `code` consumers can switch on programmatically.
554
+ */
555
+ export declare interface StructuredValidationError {
556
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
557
+ field: string;
558
+ /** Stable, snake_case error code — safe to switch on across locales. */
559
+ code: string;
560
+ /** Human-readable English message — informational only; do not regex-match. */
561
+ message: string;
562
+ }
563
+
564
+ /**
565
+ * Result of a structured validator. `errors` is an array (one entry per
566
+ * failed rule). `data` is the narrowed, typed payload, only populated on
567
+ * `valid: true`.
568
+ */
569
+ export declare interface StructuredValidationResult<T> {
570
+ valid: boolean;
571
+ errors: StructuredValidationError[];
572
+ data?: T;
573
+ }
574
+
551
575
  /**
552
576
  * Hook for managing user consent in compliance with the NDPA.
553
577
  *
@@ -632,9 +656,10 @@ declare interface UseConsentReturn {
632
656
  */
633
657
  isValid: boolean;
634
658
  /**
635
- * Validation errors (if any)
659
+ * Validation errors (if any). Each entry is a structured `{ field, code,
660
+ * message }` so consumers can switch on `code` across locales.
636
661
  */
637
- validationErrors: string[];
662
+ validationErrors: StructuredValidationError[];
638
663
  /**
639
664
  * Reset consent settings (clear from storage)
640
665
  */
@@ -646,27 +671,40 @@ declare interface UseConsentReturn {
646
671
  }
647
672
 
648
673
  /**
649
- * Validates consent settings to ensure they meet NDPA requirements
650
- * @param settings The consent settings to validate
651
- * @returns An object containing validation result and any error messages
652
- * @deprecated Use `validateConsentStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
674
+ * Validates consent options against NDPA Section 26 requirements. Each option
675
+ * is checked for a non-empty `purpose`. Failing options are reported with
676
+ * `field: 'options[i].purpose'` so consumers can map errors back to the
677
+ * originating option index.
678
+ *
679
+ * Codes emitted:
680
+ * - `options_required` — empty / missing options array
681
+ * - `purpose_required` — single option missing a purpose
653
682
  */
654
- export declare function validateConsent(settings: ConsentSettings): {
655
- valid: boolean;
656
- errors: string[];
657
- };
683
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
658
684
 
659
685
  /**
660
- * Validates that consent options meet NDPA Section 26 requirements.
661
- * Each consent option must specify a purpose for which data will be processed,
662
- * as consent must be specific and informed per the Nigeria Data Protection Act.
663
- * @param options The consent options to validate
664
- * @returns An object containing validation result and any error messages
665
- * @deprecated Use `validateConsentOptionsStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
686
+ * Validates consent settings against NDPA requirements. Returns structured
687
+ * `{ field, code, message }[]` errors so consumers can switch on `code`
688
+ * across locales without regex-matching English strings.
689
+ *
690
+ * Codes emitted:
691
+ * - `consents_required`
692
+ * - `timestamp_required`
693
+ * - `timestamp_invalid`
694
+ * - `version_required`
695
+ * - `method_required`
696
+ * - `has_interacted_required`
697
+ * - `consent_stale`
698
+ *
699
+ * @example
700
+ * ```ts
701
+ * const { valid, errors, data } = validateConsentStructured(settings);
702
+ * if (!valid) {
703
+ * const stale = errors.find((e) => e.code === 'consent_stale');
704
+ * if (stale) showRefreshBanner();
705
+ * }
706
+ * ```
666
707
  */
667
- export declare function validateConsentOptions(options: ConsentOption[]): {
668
- valid: boolean;
669
- errors: string[];
670
- };
708
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
671
709
 
672
710
  export { }
package/dist/consent.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- 'use strict';var chunkSJDDNB6M_js=require('./chunk-SJDDNB6M.js'),chunk42UJFTII_js=require('./chunk-42UJFTII.js'),chunkZD2W3YU2_js=require('./chunk-ZD2W3YU2.js'),chunk3IA3KDII_js=require('./chunk-3IA3KDII.js'),chunkROOUYQD4_js=require('./chunk-ROOUYQD4.js');require('./chunk-L2VO3MEJ.js'),require('./chunk-C2KEXHRX.js');var chunkD3HHDWBR_js=require('./chunk-D3HHDWBR.js'),chunkAME4HJR4_js=require('./chunk-AME4HJR4.js');require('./chunk-YDKWD6MQ.js'),require('./chunk-TTMGFC6C.js'),require('./chunk-VWED6UTN.js');var chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var v=react.createContext(null);function p(){let t=react.useContext(v);if(!t)throw new Error("Consent compound components must be wrapped in <Consent.Provider>. Example: <Consent.Provider options={...}><Consent.Banner /></Consent.Provider>");return t}var g=({options:t,adapter:n,version:s,onChange:r,children:c})=>{let C=chunkROOUYQD4_js.a({options:t,adapter:n,version:s,onChange:r}),o=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},C),{options:t});return jsxRuntime.jsx(v.Provider,{value:o,children:c})};var P=({classNames:t,unstyled:n})=>{let{options:s,settings:r}=p(),[c,C]=react.useState(()=>{let o={};return s.forEach(i=>{var a,_;o[i.id]=(_=(a=r==null?void 0:r.consents[i.id])!=null?a:i.defaultValue)!=null?_:false;}),o});return jsxRuntime.jsx("div",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__options-list",t==null?void 0:t.root,n),"data-ndpr-component":"consent-option-list",children:s.map(o=>jsxRuntime.jsxs("div",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__option",t==null?void 0:t.optionItem,n),children:[jsxRuntime.jsx("input",{id:`consent-${o.id}`,type:"checkbox",checked:c[o.id]||false,onChange:i=>C(a=>chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},a),{[o.id]:i.target.checked})),disabled:o.required,className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-checkbox",t==null?void 0:t.optionCheckbox,n),"aria-label":o.label}),jsxRuntime.jsxs("div",{className:n?"":"ndpr-consent-banner__option-text",children:[jsxRuntime.jsxs("label",{htmlFor:`consent-${o.id}`,className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-label",t==null?void 0:t.optionLabel,n),children:[o.label,o.required&&jsxRuntime.jsx("span",{className:n?"":"ndpr-consent-banner__required-marker",children:" *"})]}),jsxRuntime.jsx("p",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-description",t==null?void 0:t.optionDescription,n),children:o.description})]})]},o.id))})};var R=({children:t,className:n,unstyled:s})=>{let{acceptAll:r}=p();return jsxRuntime.jsx("button",{onClick:r,className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-accept-button",children:t!=null?t:"Accept All"})};var S=({children:t,className:n,unstyled:s})=>{let{rejectAll:r}=p();return jsxRuntime.jsx("button",{onClick:r,className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--secondary",n,s),"data-ndpr-component":"consent-reject-button",children:t!=null?t:"Reject All"})};var y=({children:t,className:n,unstyled:s,consents:r})=>{let{updateConsent:c,options:C}=p();return jsxRuntime.jsx("button",{onClick:()=>{if(r)c(r);else {let i={};C.forEach(a=>{i[a.id]=a.required||false;}),c(i);}},className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-save-button",children:t!=null?t:"Save Preferences"})};var I={Provider:g,OptionList:P,AcceptButton:R,RejectButton:S,SaveButton:y,Banner:chunkZD2W3YU2_js.a,Settings:chunk42UJFTII_js.a,Storage:chunkSJDDNB6M_js.a};Object.defineProperty(exports,"ConsentStorage",{enumerable:true,get:function(){return chunkSJDDNB6M_js.a}});Object.defineProperty(exports,"ConsentManager",{enumerable:true,get:function(){return chunk42UJFTII_js.a}});Object.defineProperty(exports,"ConsentBanner",{enumerable:true,get:function(){return chunkZD2W3YU2_js.a}});Object.defineProperty(exports,"appendAuditEntry",{enumerable:true,get:function(){return chunk3IA3KDII_js.c}});Object.defineProperty(exports,"createAuditEntry",{enumerable:true,get:function(){return chunk3IA3KDII_js.a}});Object.defineProperty(exports,"getAuditLog",{enumerable:true,get:function(){return chunk3IA3KDII_js.b}});Object.defineProperty(exports,"useConsent",{enumerable:true,get:function(){return chunkROOUYQD4_js.a}});Object.defineProperty(exports,"validateConsent",{enumerable:true,get:function(){return chunkD3HHDWBR_js.a}});Object.defineProperty(exports,"validateConsentOptions",{enumerable:true,get:function(){return chunkD3HHDWBR_js.c}});Object.defineProperty(exports,"resolveClass",{enumerable:true,get:function(){return chunkAME4HJR4_js.a}});exports.Consent=I;exports.ConsentAcceptButton=R;exports.ConsentOptionList=P;exports.ConsentProvider=g;exports.ConsentRejectButton=S;exports.ConsentSaveButton=y;exports.useConsentCompound=p;
2
+ 'use strict';var chunkSJDDNB6M_js=require('./chunk-SJDDNB6M.js'),chunk42UJFTII_js=require('./chunk-42UJFTII.js'),chunkZD2W3YU2_js=require('./chunk-ZD2W3YU2.js'),chunk3IA3KDII_js=require('./chunk-3IA3KDII.js'),chunkQKXGVT2Q_js=require('./chunk-QKXGVT2Q.js');require('./chunk-L2VO3MEJ.js'),require('./chunk-C2KEXHRX.js');var chunkDKLJ5DYN_js=require('./chunk-DKLJ5DYN.js'),chunkAME4HJR4_js=require('./chunk-AME4HJR4.js');require('./chunk-YDKWD6MQ.js'),require('./chunk-TTMGFC6C.js'),require('./chunk-VWED6UTN.js');var chunkRFPLZDIO_js=require('./chunk-RFPLZDIO.js'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var v=react.createContext(null);function p(){let t=react.useContext(v);if(!t)throw new Error("Consent compound components must be wrapped in <Consent.Provider>. Example: <Consent.Provider options={...}><Consent.Banner /></Consent.Provider>");return t}var g=({options:t,adapter:n,version:s,onChange:r,children:c})=>{let d=chunkQKXGVT2Q_js.a({options:t,adapter:n,version:s,onChange:r}),o=chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},d),{options:t});return jsxRuntime.jsx(v.Provider,{value:o,children:c})};var S=({classNames:t,unstyled:n})=>{let{options:s,settings:r}=p(),[c,d]=react.useState(()=>{let o={};return s.forEach(i=>{var a,_;o[i.id]=(_=(a=r==null?void 0:r.consents[i.id])!=null?a:i.defaultValue)!=null?_:false;}),o});return jsxRuntime.jsx("div",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__options-list",t==null?void 0:t.root,n),"data-ndpr-component":"consent-option-list",children:s.map(o=>jsxRuntime.jsxs("div",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__option",t==null?void 0:t.optionItem,n),children:[jsxRuntime.jsx("input",{id:`consent-${o.id}`,type:"checkbox",checked:c[o.id]||false,onChange:i=>d(a=>chunkRFPLZDIO_js.b(chunkRFPLZDIO_js.a({},a),{[o.id]:i.target.checked})),disabled:o.required,className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-checkbox",t==null?void 0:t.optionCheckbox,n),"aria-label":o.label}),jsxRuntime.jsxs("div",{className:n?"":"ndpr-consent-banner__option-text",children:[jsxRuntime.jsxs("label",{htmlFor:`consent-${o.id}`,className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-label",t==null?void 0:t.optionLabel,n),children:[o.label,o.required&&jsxRuntime.jsx("span",{className:n?"":"ndpr-consent-banner__required-marker",children:" *"})]}),jsxRuntime.jsx("p",{className:chunkAME4HJR4_js.a("ndpr-consent-banner__option-description",t==null?void 0:t.optionDescription,n),children:o.description})]})]},o.id))})};var P=({children:t,className:n,unstyled:s})=>{let{acceptAll:r}=p();return jsxRuntime.jsx("button",{onClick:r,className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-accept-button",children:t!=null?t:"Accept All"})};var R=({children:t,className:n,unstyled:s})=>{let{rejectAll:r}=p();return jsxRuntime.jsx("button",{onClick:r,className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--secondary",n,s),"data-ndpr-component":"consent-reject-button",children:t!=null?t:"Reject All"})};var y=({children:t,className:n,unstyled:s,consents:r})=>{let{updateConsent:c,options:d}=p();return jsxRuntime.jsx("button",{onClick:()=>{if(r)c(r);else {let i={};d.forEach(a=>{i[a.id]=a.required||false;}),c(i);}},className:chunkAME4HJR4_js.a("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-save-button",children:t!=null?t:"Save Preferences"})};var I={Provider:g,OptionList:S,AcceptButton:P,RejectButton:R,SaveButton:y,Banner:chunkZD2W3YU2_js.a,Settings:chunk42UJFTII_js.a,Storage:chunkSJDDNB6M_js.a};Object.defineProperty(exports,"ConsentStorage",{enumerable:true,get:function(){return chunkSJDDNB6M_js.a}});Object.defineProperty(exports,"ConsentManager",{enumerable:true,get:function(){return chunk42UJFTII_js.a}});Object.defineProperty(exports,"ConsentBanner",{enumerable:true,get:function(){return chunkZD2W3YU2_js.a}});Object.defineProperty(exports,"appendAuditEntry",{enumerable:true,get:function(){return chunk3IA3KDII_js.c}});Object.defineProperty(exports,"createAuditEntry",{enumerable:true,get:function(){return chunk3IA3KDII_js.a}});Object.defineProperty(exports,"getAuditLog",{enumerable:true,get:function(){return chunk3IA3KDII_js.b}});Object.defineProperty(exports,"useConsent",{enumerable:true,get:function(){return chunkQKXGVT2Q_js.a}});Object.defineProperty(exports,"validateConsentOptionsStructured",{enumerable:true,get:function(){return chunkDKLJ5DYN_js.b}});Object.defineProperty(exports,"validateConsentStructured",{enumerable:true,get:function(){return chunkDKLJ5DYN_js.a}});Object.defineProperty(exports,"resolveClass",{enumerable:true,get:function(){return chunkAME4HJR4_js.a}});exports.Consent=I;exports.ConsentAcceptButton=P;exports.ConsentOptionList=S;exports.ConsentProvider=g;exports.ConsentRejectButton=R;exports.ConsentSaveButton=y;exports.useConsentCompound=p;
package/dist/consent.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import {a}from'./chunk-XOH4WXOZ.mjs';export{a as ConsentStorage}from'./chunk-XOH4WXOZ.mjs';import {a as a$1}from'./chunk-BPMVJOHH.mjs';export{a as ConsentManager}from'./chunk-BPMVJOHH.mjs';import {a as a$2}from'./chunk-I2ZBMR7F.mjs';export{a as ConsentBanner}from'./chunk-I2ZBMR7F.mjs';export{c as appendAuditEntry,a as createAuditEntry,b as getAuditLog}from'./chunk-V7UFP6QU.mjs';import {a as a$3}from'./chunk-7Z7NURIA.mjs';export{a as useConsent}from'./chunk-7Z7NURIA.mjs';import'./chunk-YTU4FNM2.mjs';import'./chunk-XC3DLYEG.mjs';export{a as validateConsent,c as validateConsentOptions}from'./chunk-NI54X543.mjs';import {a as a$5}from'./chunk-SFGW37LE.mjs';export{a as resolveClass}from'./chunk-SFGW37LE.mjs';import'./chunk-FRQFU44F.mjs';import'./chunk-V5TZJJWU.mjs';import'./chunk-DBZSN4WP.mjs';import {b,a as a$4}from'./chunk-ZJYULEER.mjs';import {createContext,useContext,useState}from'react';import {jsx,jsxs}from'react/jsx-runtime';var v=createContext(null);function p(){let t=useContext(v);if(!t)throw new Error("Consent compound components must be wrapped in <Consent.Provider>. Example: <Consent.Provider options={...}><Consent.Banner /></Consent.Provider>");return t}var g=({options:t,adapter:n,version:s,onChange:r,children:c})=>{let C=a$3({options:t,adapter:n,version:s,onChange:r}),o=b(a$4({},C),{options:t});return jsx(v.Provider,{value:o,children:c})};var P=({classNames:t,unstyled:n})=>{let{options:s,settings:r}=p(),[c,C]=useState(()=>{let o={};return s.forEach(i=>{var a,_;o[i.id]=(_=(a=r==null?void 0:r.consents[i.id])!=null?a:i.defaultValue)!=null?_:false;}),o});return jsx("div",{className:a$5("ndpr-consent-banner__options-list",t==null?void 0:t.root,n),"data-ndpr-component":"consent-option-list",children:s.map(o=>jsxs("div",{className:a$5("ndpr-consent-banner__option",t==null?void 0:t.optionItem,n),children:[jsx("input",{id:`consent-${o.id}`,type:"checkbox",checked:c[o.id]||false,onChange:i=>C(a=>b(a$4({},a),{[o.id]:i.target.checked})),disabled:o.required,className:a$5("ndpr-consent-banner__option-checkbox",t==null?void 0:t.optionCheckbox,n),"aria-label":o.label}),jsxs("div",{className:n?"":"ndpr-consent-banner__option-text",children:[jsxs("label",{htmlFor:`consent-${o.id}`,className:a$5("ndpr-consent-banner__option-label",t==null?void 0:t.optionLabel,n),children:[o.label,o.required&&jsx("span",{className:n?"":"ndpr-consent-banner__required-marker",children:" *"})]}),jsx("p",{className:a$5("ndpr-consent-banner__option-description",t==null?void 0:t.optionDescription,n),children:o.description})]})]},o.id))})};var R=({children:t,className:n,unstyled:s})=>{let{acceptAll:r}=p();return jsx("button",{onClick:r,className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-accept-button",children:t!=null?t:"Accept All"})};var S=({children:t,className:n,unstyled:s})=>{let{rejectAll:r}=p();return jsx("button",{onClick:r,className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--secondary",n,s),"data-ndpr-component":"consent-reject-button",children:t!=null?t:"Reject All"})};var y=({children:t,className:n,unstyled:s,consents:r})=>{let{updateConsent:c,options:C}=p();return jsx("button",{onClick:()=>{if(r)c(r);else {let i={};C.forEach(a=>{i[a.id]=a.required||false;}),c(i);}},className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-save-button",children:t!=null?t:"Save Preferences"})};var I={Provider:g,OptionList:P,AcceptButton:R,RejectButton:S,SaveButton:y,Banner:a$2,Settings:a$1,Storage:a};export{I as Consent,R as ConsentAcceptButton,P as ConsentOptionList,g as ConsentProvider,S as ConsentRejectButton,y as ConsentSaveButton,p as useConsentCompound};
2
+ import {a}from'./chunk-XOH4WXOZ.mjs';export{a as ConsentStorage}from'./chunk-XOH4WXOZ.mjs';import {a as a$1}from'./chunk-BPMVJOHH.mjs';export{a as ConsentManager}from'./chunk-BPMVJOHH.mjs';import {a as a$2}from'./chunk-I2ZBMR7F.mjs';export{a as ConsentBanner}from'./chunk-I2ZBMR7F.mjs';export{c as appendAuditEntry,a as createAuditEntry,b as getAuditLog}from'./chunk-V7UFP6QU.mjs';import {a as a$3}from'./chunk-PQ5IPUJN.mjs';export{a as useConsent}from'./chunk-PQ5IPUJN.mjs';import'./chunk-YTU4FNM2.mjs';import'./chunk-XC3DLYEG.mjs';export{b as validateConsentOptionsStructured,a as validateConsentStructured}from'./chunk-R3ZKV2J7.mjs';import {a as a$5}from'./chunk-SFGW37LE.mjs';export{a as resolveClass}from'./chunk-SFGW37LE.mjs';import'./chunk-FRQFU44F.mjs';import'./chunk-V5TZJJWU.mjs';import'./chunk-DBZSN4WP.mjs';import {b,a as a$4}from'./chunk-ZJYULEER.mjs';import {createContext,useContext,useState}from'react';import {jsx,jsxs}from'react/jsx-runtime';var v=createContext(null);function p(){let t=useContext(v);if(!t)throw new Error("Consent compound components must be wrapped in <Consent.Provider>. Example: <Consent.Provider options={...}><Consent.Banner /></Consent.Provider>");return t}var g=({options:t,adapter:n,version:s,onChange:r,children:c})=>{let d=a$3({options:t,adapter:n,version:s,onChange:r}),o=b(a$4({},d),{options:t});return jsx(v.Provider,{value:o,children:c})};var S=({classNames:t,unstyled:n})=>{let{options:s,settings:r}=p(),[c,d]=useState(()=>{let o={};return s.forEach(i=>{var a,_;o[i.id]=(_=(a=r==null?void 0:r.consents[i.id])!=null?a:i.defaultValue)!=null?_:false;}),o});return jsx("div",{className:a$5("ndpr-consent-banner__options-list",t==null?void 0:t.root,n),"data-ndpr-component":"consent-option-list",children:s.map(o=>jsxs("div",{className:a$5("ndpr-consent-banner__option",t==null?void 0:t.optionItem,n),children:[jsx("input",{id:`consent-${o.id}`,type:"checkbox",checked:c[o.id]||false,onChange:i=>d(a=>b(a$4({},a),{[o.id]:i.target.checked})),disabled:o.required,className:a$5("ndpr-consent-banner__option-checkbox",t==null?void 0:t.optionCheckbox,n),"aria-label":o.label}),jsxs("div",{className:n?"":"ndpr-consent-banner__option-text",children:[jsxs("label",{htmlFor:`consent-${o.id}`,className:a$5("ndpr-consent-banner__option-label",t==null?void 0:t.optionLabel,n),children:[o.label,o.required&&jsx("span",{className:n?"":"ndpr-consent-banner__required-marker",children:" *"})]}),jsx("p",{className:a$5("ndpr-consent-banner__option-description",t==null?void 0:t.optionDescription,n),children:o.description})]})]},o.id))})};var P=({children:t,className:n,unstyled:s})=>{let{acceptAll:r}=p();return jsx("button",{onClick:r,className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-accept-button",children:t!=null?t:"Accept All"})};var R=({children:t,className:n,unstyled:s})=>{let{rejectAll:r}=p();return jsx("button",{onClick:r,className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--secondary",n,s),"data-ndpr-component":"consent-reject-button",children:t!=null?t:"Reject All"})};var y=({children:t,className:n,unstyled:s,consents:r})=>{let{updateConsent:c,options:d}=p();return jsx("button",{onClick:()=>{if(r)c(r);else {let i={};d.forEach(a=>{i[a.id]=a.required||false;}),c(i);}},className:a$5("ndpr-consent-banner__button ndpr-consent-banner__button--primary",n,s),"data-ndpr-component":"consent-save-button",children:t!=null?t:"Save Preferences"})};var I={Provider:g,OptionList:S,AcceptButton:P,RejectButton:R,SaveButton:y,Banner:a$2,Settings:a$1,Storage:a};export{I as Consent,P as ConsentAcceptButton,S as ConsentOptionList,g as ConsentProvider,R as ConsentRejectButton,y as ConsentSaveButton,p as useConsentCompound};
package/dist/core.d.mts CHANGED
@@ -815,7 +815,7 @@ export declare type DSRStatus = 'pending' | 'awaitingVerification' | 'inProgress
815
815
  /**
816
816
  * Validated DSR submission shape — matches what `<DSRRequestForm onSubmit>`
817
817
  * emits client-side. Use this as the typed parameter for your server-side
818
- * handler after `validateDsrSubmission` returns `valid: true`.
818
+ * handler after `validateDsrSubmissionStructured` returns `valid: true`.
819
819
  */
820
820
  export declare interface DsrSubmissionPayload {
821
821
  requestType: string;
@@ -830,16 +830,6 @@ export declare interface DsrSubmissionPayload {
830
830
  submittedAt: number;
831
831
  }
832
832
 
833
- /** Result of validating a raw DSR submission payload. */
834
- export declare interface DsrSubmissionValidationResult {
835
- /** True when the payload conforms to the DSR submission contract. */
836
- valid: boolean;
837
- /** Field-keyed error messages. Empty when `valid` is true. */
838
- errors: Record<string, string>;
839
- /** The narrowed, typed payload — only populated when `valid` is true. */
840
- data?: DsrSubmissionPayload;
841
- }
842
-
843
833
  /**
844
834
  * Data Subject Rights types aligned with NDPA 2023 Part VI (Sections 34-38)
845
835
  * and the related provisions in Part V (Section 27 — information to the data subject)
@@ -936,16 +926,29 @@ export declare function exportROPAToCSV(ropa: RecordOfProcessingActivities): str
936
926
  export declare function findUnfilledTokens(rendered: string): string[];
937
927
 
938
928
  /**
939
- * Formats a DSR request for display or submission
940
- * @param request The DSR request to format
941
- * @returns Formatted request data
942
- * @deprecated Use `formatDSRRequestStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
943
- */
944
- export declare function formatDSRRequest(request: DSRRequest): {
929
+ * Format a DSR request for display or submission. Returns the formatted
930
+ * payload plus a typed `errors` array of `{ field, code, message }` for any
931
+ * required fields missing from the source request.
932
+ *
933
+ * Codes emitted:
934
+ * - `request_id_required`
935
+ * - `request_type_required`
936
+ * - `request_status_required`
937
+ * - `created_at_required`
938
+ * - `subject_name_required`
939
+ * - `subject_email_required`
940
+ */
941
+ export declare function formatDSRRequestStructured(request: DSRRequest): FormatDSRRequestStructuredResult;
942
+
943
+ /** Result of {@link formatDSRRequestStructured}. */
944
+ export declare interface FormatDSRRequestStructuredResult {
945
+ valid: boolean;
946
+ errors: StructuredValidationError[];
947
+ /** Formatted request payload — always populated regardless of `valid`. */
945
948
  formattedRequest: Record<string, unknown>;
946
- isValid: boolean;
947
- validationErrors: string[];
948
- };
949
+ /** Narrowed input — populated only on `valid: true`. */
950
+ data?: DSRRequest;
951
+ }
949
952
 
950
953
  export declare const frenchLocale: Required<{
951
954
  [K in keyof NDPRLocale]: Required<NonNullable<NDPRLocale[K]>>;
@@ -2016,6 +2019,30 @@ export declare interface StorageAdapter<T = unknown> {
2016
2019
  remove(): void | Promise<void>;
2017
2020
  }
2018
2021
 
2022
+ /**
2023
+ * Single structured validation error with a stable, locale-independent
2024
+ * `code` consumers can switch on programmatically.
2025
+ */
2026
+ export declare interface StructuredValidationError {
2027
+ /** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
2028
+ field: string;
2029
+ /** Stable, snake_case error code — safe to switch on across locales. */
2030
+ code: string;
2031
+ /** Human-readable English message — informational only; do not regex-match. */
2032
+ message: string;
2033
+ }
2034
+
2035
+ /**
2036
+ * Result of a structured validator. `errors` is an array (one entry per
2037
+ * failed rule). `data` is the narrowed, typed payload, only populated on
2038
+ * `valid: true`.
2039
+ */
2040
+ export declare interface StructuredValidationResult<T> {
2041
+ valid: boolean;
2042
+ errors: StructuredValidationError[];
2043
+ data?: T;
2044
+ }
2045
+
2019
2046
  /** Full context used to generate an adaptive privacy policy. */
2020
2047
  export declare interface TemplateContext {
2021
2048
  /** Organisation details, extended with industry and size. */
@@ -2155,70 +2182,43 @@ export declare function useNDPRConfig(): NDPRConfig;
2155
2182
  export declare function useNDPRLocale(): typeof defaultLocale;
2156
2183
 
2157
2184
  /**
2158
- * Validates consent settings to ensure they meet NDPA requirements
2159
- * @param settings The consent settings to validate
2160
- * @returns An object containing validation result and any error messages
2161
- * @deprecated Use `validateConsentStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
2185
+ * Validates consent options against NDPA Section 26 requirements. Each option
2186
+ * is checked for a non-empty `purpose`. Failing options are reported with
2187
+ * `field: 'options[i].purpose'` so consumers can map errors back to the
2188
+ * originating option index.
2189
+ *
2190
+ * Codes emitted:
2191
+ * - `options_required` — empty / missing options array
2192
+ * - `purpose_required` — single option missing a purpose
2162
2193
  */
2163
- export declare function validateConsent(settings: ConsentSettings): {
2164
- valid: boolean;
2165
- errors: string[];
2166
- };
2194
+ export declare function validateConsentOptionsStructured(options: ConsentOption[]): StructuredValidationResult<ConsentOption[]>;
2167
2195
 
2168
2196
  /**
2169
- * Validates that consent options meet NDPA Section 26 requirements.
2170
- * Each consent option must specify a purpose for which data will be processed,
2171
- * as consent must be specific and informed per the Nigeria Data Protection Act.
2172
- * @param options The consent options to validate
2173
- * @returns An object containing validation result and any error messages
2174
- * @deprecated Use `validateConsentOptionsStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
2175
- */
2176
- export declare function validateConsentOptions(options: ConsentOption[]): {
2177
- valid: boolean;
2178
- errors: string[];
2179
- };
2180
-
2181
- /**
2182
- * Validate a raw DSR submission payload against the same rules
2183
- * `<DSRRequestForm />` enforces client-side. Designed to be called from a
2184
- * server-side handler (Next.js Route Handler, NestJS controller, Express
2185
- * middleware, Cloudflare Worker) so client and server stay in sync without
2186
- * the consumer hand-rolling zod / class-validator schemas.
2197
+ * Validates consent settings against NDPA requirements. Returns structured
2198
+ * `{ field, code, message }[]` errors so consumers can switch on `code`
2199
+ * across locales without regex-matching English strings.
2187
2200
  *
2188
- * Defensive — accepts `unknown` and narrows. Safe to call directly on
2189
- * `await request.json()`.
2201
+ * Codes emitted:
2202
+ * - `consents_required`
2203
+ * - `timestamp_required`
2204
+ * - `timestamp_invalid`
2205
+ * - `version_required`
2206
+ * - `method_required`
2207
+ * - `has_interacted_required`
2208
+ * - `consent_stale`
2190
2209
  *
2191
- * @example **Next.js Route Handler**
2210
+ * @example
2192
2211
  * ```ts
2193
- * // app/api/dsr/route.ts
2194
- * import { validateDsrSubmission } from '@tantainnovative/ndpr-toolkit/server';
2195
- *
2196
- * export async function POST(req: Request) {
2197
- * const { valid, errors, data } = validateDsrSubmission(await req.json());
2198
- * if (!valid) return Response.json({ errors }, { status: 422 });
2199
- * // `data` is the typed DsrSubmissionPayload
2200
- * await dsrStore.create(data);
2201
- * return Response.json({ ok: true }, { status: 201 });
2212
+ * const { valid, errors, data } = validateConsentStructured(settings);
2213
+ * if (!valid) {
2214
+ * const stale = errors.find((e) => e.code === 'consent_stale');
2215
+ * if (stale) showRefreshBanner();
2202
2216
  * }
2203
2217
  * ```
2204
- *
2205
- * @example **Lock to specific request types**
2206
- * ```ts
2207
- * validateDsrSubmission(payload, {
2208
- * allowedRequestTypes: ['access', 'erasure', 'rectification'],
2209
- * });
2210
- * ```
2211
- *
2212
- * @example **Skip identity verification (e.g. authenticated session)**
2213
- * ```ts
2214
- * validateDsrSubmission(payload, { requireIdentityVerification: false });
2215
- * ```
2216
- *
2217
- * @deprecated Use `validateDsrSubmissionStructured()` for typed `{ field, code, message }[]` errors. The legacy string-returning shape will be removed in 5.0.
2218
2218
  */
2219
- export declare function validateDsrSubmission(payload: unknown, options?: ValidateDsrSubmissionOptions): DsrSubmissionValidationResult;
2219
+ export declare function validateConsentStructured(settings: ConsentSettings): StructuredValidationResult<ConsentSettings>;
2220
2220
 
2221
- /** Options for {@link validateDsrSubmission}. */
2221
+ /** Options for {@link validateDsrSubmissionStructured}. */
2222
2222
  export declare interface ValidateDsrSubmissionOptions {
2223
2223
  /**
2224
2224
  * Whether the data subject is required to provide an identifier
@@ -2235,6 +2235,48 @@ export declare interface ValidateDsrSubmissionOptions {
2235
2235
  allowedRequestTypes?: string[];
2236
2236
  }
2237
2237
 
2238
+ /**
2239
+ * Validate a raw DSR submission payload against the same rules
2240
+ * `<DSRRequestForm />` enforces client-side. Designed to be called from a
2241
+ * server-side handler (Next.js Route Handler, NestJS controller, Express
2242
+ * middleware, Cloudflare Worker) so client and server stay in sync without
2243
+ * the consumer hand-rolling zod / class-validator schemas.
2244
+ *
2245
+ * Defensive — accepts `unknown` and narrows. Safe to call directly on
2246
+ * `await request.json()`. Returns `{ field, code, message }[]` errors so
2247
+ * callers can switch on `code` programmatically across locales.
2248
+ *
2249
+ * Codes emitted:
2250
+ * - `payload_not_object`
2251
+ * - `request_type_required`
2252
+ * - `request_type_not_allowed`
2253
+ * - `data_subject_required`
2254
+ * - `full_name_required`
2255
+ * - `email_required`
2256
+ * - `email_invalid_format`
2257
+ * - `phone_invalid_type`
2258
+ * - `identifier_type_required`
2259
+ * - `identifier_value_required`
2260
+ * - `submitted_at_invalid`
2261
+ * - `additional_info_invalid_type`
2262
+ * - `payload_final_narrowing_failed`
2263
+ *
2264
+ * @example **Next.js Route Handler**
2265
+ * ```ts
2266
+ * import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
2267
+ *
2268
+ * export async function POST(req: Request) {
2269
+ * const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
2270
+ * if (!valid) {
2271
+ * return Response.json({ errors }, { status: 422 });
2272
+ * }
2273
+ * await dsrStore.create(data);
2274
+ * return Response.json({ ok: true }, { status: 201 });
2275
+ * }
2276
+ * ```
2277
+ */
2278
+ export declare function validateDsrSubmissionStructured(payload: unknown, options?: ValidateDsrSubmissionOptions): StructuredValidationResult<DsrSubmissionPayload>;
2279
+
2238
2280
  /**
2239
2281
  * Validates that all required fields are present on a processing activity
2240
2282
  * and that the lawful basis is properly documented.