@tantainnovative/ndpr-toolkit 1.0.2 → 1.0.3
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/.claude/settings.local.json +20 -0
- package/.eslintrc.json +10 -0
- package/.github/workflows/ci.yml +36 -0
- package/.github/workflows/nextjs.yml +104 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc.js +4 -0
- package/.nvmrc +1 -0
- package/.versionrc +17 -0
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +90 -0
- package/CNAME +1 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +84 -447
- package/RELEASE-NOTES-v1.0.0.md +140 -0
- package/RELEASE-NOTES-v1.0.1.md +69 -0
- package/SECURITY.md +21 -0
- package/commitlint.config.js +36 -0
- package/components.json +21 -0
- package/eslint.config.mjs +16 -0
- package/jest.config.js +31 -0
- package/jest.setup.js +15 -0
- package/next.config.js +15 -0
- package/next.config.ts +62 -0
- package/package.json +70 -52
- package/packages/ndpr-toolkit/README.md +467 -0
- package/packages/ndpr-toolkit/jest.config.js +23 -0
- package/packages/ndpr-toolkit/package-lock.json +8197 -0
- package/packages/ndpr-toolkit/package.json +71 -0
- package/packages/ndpr-toolkit/rollup.config.js +34 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
- package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
- package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
- package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
- package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
- package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
- package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
- package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
- package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +13 -0
- package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
- package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
- package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
- package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
- package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
- package/packages/ndpr-toolkit/src/types/index.ts +42 -0
- package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
- package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
- package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
- package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
- package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
- package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
- package/packages/ndpr-toolkit/tsconfig.json +23 -0
- package/postcss.config.mjs +5 -0
- package/public/NDPR TOOLKIT.svg +1 -0
- package/public/favicon/android-chrome-192x192.png +0 -0
- package/public/favicon/android-chrome-512x512.png +0 -0
- package/public/favicon/apple-touch-icon.png +0 -0
- package/public/favicon/favicon-16x16.png +0 -0
- package/public/favicon/favicon-32x32.png +0 -0
- package/public/favicon/site.webmanifest +1 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/ndpr-toolkit-logo.svg +108 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/__tests__/example.test.ts +13 -0
- package/src/__tests__/requestService.test.ts +57 -0
- package/src/app/accessibility.css +70 -0
- package/src/app/docs/components/DocLayout.tsx +267 -0
- package/src/app/docs/components/breach-notification/page.tsx +797 -0
- package/src/app/docs/components/consent-management/page.tsx +576 -0
- package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
- package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
- package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
- package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
- package/src/app/docs/components/hooks/page.tsx +305 -0
- package/src/app/docs/components/page.tsx +84 -0
- package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
- package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
- package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
- package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
- package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
- package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
- package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
- package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
- package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
- package/src/app/docs/guides/managing-consent/page.tsx +738 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
- package/src/app/docs/guides/page.tsx +67 -0
- package/src/app/docs/layout.tsx +15 -0
- package/src/app/docs/metadata.ts +31 -0
- package/src/app/docs/page.tsx +572 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +123 -0
- package/src/app/layout.tsx +37 -0
- package/src/app/ndpr-demos/breach/page.tsx +354 -0
- package/src/app/ndpr-demos/consent/page.tsx +366 -0
- package/src/app/ndpr-demos/dpia/page.tsx +495 -0
- package/src/app/ndpr-demos/dsr/page.tsx +280 -0
- package/src/app/ndpr-demos/page.tsx +73 -0
- package/src/app/ndpr-demos/policy/page.tsx +771 -0
- package/src/app/page.tsx +452 -0
- package/src/components/ErrorBoundary.tsx +90 -0
- package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
- package/src/components/consent/ConsentBanner.tsx +159 -0
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
- package/src/components/docs/DocLayout.tsx +289 -0
- package/src/components/docs/index.ts +2 -0
- package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
- package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
- package/src/components/privacy-policy/data.ts +98 -0
- package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
- package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
- package/src/components/privacy-policy/shared/FormField.tsx +79 -0
- package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
- package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
- package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
- package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
- package/src/components/ui/Badge.tsx +46 -0
- package/src/components/ui/Button.tsx +59 -0
- package/src/components/ui/Card.tsx +92 -0
- package/src/components/ui/Checkbox.tsx +57 -0
- package/src/components/ui/FormField.tsx +50 -0
- package/src/components/ui/Input.tsx +38 -0
- package/src/components/ui/Loading.tsx +201 -0
- package/src/components/ui/Select.tsx +42 -0
- package/src/components/ui/TextArea.tsx +38 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/hooks/useConsent.ts +64 -0
- package/src/hooks/useLoadingState.ts +85 -0
- package/src/lib/consentService.ts +137 -0
- package/src/lib/dpiaQuestions.ts +148 -0
- package/src/lib/requestService.ts +75 -0
- package/src/lib/sanitize.ts +108 -0
- package/src/lib/storage.ts +222 -0
- package/src/lib/utils.ts +6 -0
- package/src/types/html-to-docx.d.ts +30 -0
- package/src/types/index.ts +72 -0
- package/tailwind.config.ts +65 -0
- package/tsconfig.json +41 -0
- package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
- package/dist/components/breach/BreachReportForm.d.ts +0 -66
- package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
- package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
- package/dist/components/consent/ConsentBanner.d.ts +0 -79
- package/dist/components/consent/ConsentManager.d.ts +0 -73
- package/dist/components/consent/ConsentStorage.d.ts +0 -41
- package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
- package/dist/components/dpia/DPIAReport.d.ts +0 -40
- package/dist/components/dpia/StepIndicator.d.ts +0 -64
- package/dist/components/dsr/DSRDashboard.d.ts +0 -58
- package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
- package/dist/components/dsr/DSRTracker.d.ts +0 -56
- package/dist/components/policy/PolicyExporter.d.ts +0 -65
- package/dist/components/policy/PolicyGenerator.d.ts +0 -54
- package/dist/components/policy/PolicyPreview.d.ts +0 -71
- package/dist/hooks/useBreach.d.ts +0 -97
- package/dist/hooks/useConsent.d.ts +0 -63
- package/dist/hooks/useDPIA.d.ts +0 -92
- package/dist/hooks/useDSR.d.ts +0 -72
- package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
- package/dist/index.esm.js +0 -2
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/setupTests.d.ts +0 -2
- package/dist/types/breach.d.ts +0 -239
- package/dist/types/consent.d.ts +0 -95
- package/dist/types/dpia.d.ts +0 -196
- package/dist/types/dsr.d.ts +0 -162
- package/dist/types/privacy.d.ts +0 -204
- package/dist/utils/breach.d.ts +0 -14
- package/dist/utils/consent.d.ts +0 -10
- package/dist/utils/dpia.d.ts +0 -12
- package/dist/utils/dsr.d.ts +0 -11
- package/dist/utils/privacy.d.ts +0 -12
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { DocLayout } from '../DocLayout';
|
|
5
|
+
import { Button } from '@/components/ui/Button';
|
|
6
|
+
import { Card, CardContent } from '@/components/ui/Card';
|
|
7
|
+
|
|
8
|
+
export default function ConsentManagementDocs() {
|
|
9
|
+
return (
|
|
10
|
+
<DocLayout
|
|
11
|
+
title="Consent Management"
|
|
12
|
+
description="NDPR-compliant consent management system for handling user consent preferences"
|
|
13
|
+
>
|
|
14
|
+
<div className="flex mb-6 space-x-2">
|
|
15
|
+
<Button asChild variant="outline" size="sm">
|
|
16
|
+
<Link href="/gdrp-demos/consent">
|
|
17
|
+
View Demo
|
|
18
|
+
</Link>
|
|
19
|
+
</Button>
|
|
20
|
+
<Button asChild variant="outline" size="sm">
|
|
21
|
+
<a href="https://github.com/tantainnovative/ndpr-toolkit/tree/main/src/components/consent" target="_blank" rel="noopener noreferrer">
|
|
22
|
+
View Source
|
|
23
|
+
</a>
|
|
24
|
+
</Button>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<section id="overview" className="mb-8">
|
|
28
|
+
<h2 className="text-2xl font-bold mb-4">Overview</h2>
|
|
29
|
+
<p className="mb-4">
|
|
30
|
+
The Consent Management component provides a complete solution for collecting, storing, and managing user consent
|
|
31
|
+
in compliance with the Nigeria Data Protection Regulation (NDPR). It includes a customizable consent banner,
|
|
32
|
+
preference management interface, and consent storage system.
|
|
33
|
+
</p>
|
|
34
|
+
<div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-md">
|
|
35
|
+
<h4 className="text-blue-800 dark:text-blue-200 font-medium mb-2">NDPR Consent Requirements</h4>
|
|
36
|
+
<p className="text-blue-700 dark:text-blue-300 text-sm mb-0">
|
|
37
|
+
Under the NDPR, consent must be freely given, specific, informed, and unambiguous. The data subject must clearly
|
|
38
|
+
indicate acceptance through a statement or clear affirmative action. Pre-ticked boxes or silence do not constitute valid consent.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
43
|
+
<section id="installation" className="mb-8">
|
|
44
|
+
<h2 className="text-2xl font-bold mb-4">Installation</h2>
|
|
45
|
+
<p className="mb-4">
|
|
46
|
+
Install the NDPR Toolkit package which includes the Consent Management components:
|
|
47
|
+
</p>
|
|
48
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto mb-4">
|
|
49
|
+
<pre><code>npm install @tantainnovative/ndpr-toolkit</code></pre>
|
|
50
|
+
</div>
|
|
51
|
+
<p>
|
|
52
|
+
Or if you're using yarn:
|
|
53
|
+
</p>
|
|
54
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
55
|
+
<pre><code>yarn add @tantainnovative/ndpr-toolkit</code></pre>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<section id="components" className="mb-8">
|
|
60
|
+
<h2 className="text-2xl font-bold mb-4">Components</h2>
|
|
61
|
+
<p className="mb-4">
|
|
62
|
+
The Consent Management system includes several components that work together:
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<div className="space-y-6">
|
|
66
|
+
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
|
67
|
+
<h3 className="text-xl font-bold mb-2">ConsentBanner</h3>
|
|
68
|
+
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
|
69
|
+
A cookie consent banner that appears at the bottom of the page when a user first visits your site. Fully customizable with support for multiple consent options.
|
|
70
|
+
</p>
|
|
71
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
72
|
+
<pre><code>{`import { ConsentBanner } from '@tantainnovative/ndpr-toolkit';
|
|
73
|
+
|
|
74
|
+
<ConsentBanner
|
|
75
|
+
options={[
|
|
76
|
+
{
|
|
77
|
+
id: 'necessary',
|
|
78
|
+
label: 'Necessary Cookies',
|
|
79
|
+
description: 'Essential cookies for the website to function.',
|
|
80
|
+
required: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'analytics',
|
|
84
|
+
label: 'Analytics Cookies',
|
|
85
|
+
description: 'Cookies that help us understand how you use our website.',
|
|
86
|
+
required: false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'marketing',
|
|
90
|
+
label: 'Marketing Cookies',
|
|
91
|
+
description: 'Cookies used for marketing purposes.',
|
|
92
|
+
required: false
|
|
93
|
+
}
|
|
94
|
+
]}
|
|
95
|
+
onSave={(consents) => console.log(consents)}
|
|
96
|
+
position="bottom"
|
|
97
|
+
showPreferences={true}
|
|
98
|
+
privacyPolicyUrl="/privacy-policy"
|
|
99
|
+
/>`}</code></pre>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
|
104
|
+
<h3 className="text-xl font-bold mb-2">ConsentPreferences</h3>
|
|
105
|
+
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
|
106
|
+
A detailed interface for users to manage their consent preferences after the initial consent has been given.
|
|
107
|
+
</p>
|
|
108
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
109
|
+
<pre><code>{`import { ConsentPreferences } from '@tantainnovative/ndpr-toolkit';
|
|
110
|
+
|
|
111
|
+
<ConsentPreferences
|
|
112
|
+
options={consentOptions}
|
|
113
|
+
currentConsent={currentConsent}
|
|
114
|
+
onSave={handleSaveConsent}
|
|
115
|
+
onReset={handleResetConsent}
|
|
116
|
+
/>`}</code></pre>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
|
121
|
+
<h3 className="text-xl font-bold mb-2">ConsentManager</h3>
|
|
122
|
+
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
|
123
|
+
A higher-order component that manages the consent state and provides methods for checking and updating consent. Works with the useConsent hook to provide a complete consent management solution.
|
|
124
|
+
</p>
|
|
125
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
126
|
+
<pre><code>{`import { ConsentManager, useConsent } from '@tantainnovative/ndpr-toolkit';
|
|
127
|
+
|
|
128
|
+
function App() {
|
|
129
|
+
return (
|
|
130
|
+
<ConsentManager
|
|
131
|
+
options={[
|
|
132
|
+
{
|
|
133
|
+
id: 'necessary',
|
|
134
|
+
label: 'Necessary Cookies',
|
|
135
|
+
description: 'Essential cookies for the website to function.',
|
|
136
|
+
required: true
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'analytics',
|
|
140
|
+
label: 'Analytics Cookies',
|
|
141
|
+
description: 'Cookies that help us understand how you use our website.',
|
|
142
|
+
required: false
|
|
143
|
+
}
|
|
144
|
+
]}
|
|
145
|
+
storageKey="my-app-consent"
|
|
146
|
+
autoLoad={true}
|
|
147
|
+
autoSave={true}
|
|
148
|
+
>
|
|
149
|
+
<MyApp />
|
|
150
|
+
</ConsentManager>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function MyApp() {
|
|
155
|
+
const {
|
|
156
|
+
consents,
|
|
157
|
+
hasConsented,
|
|
158
|
+
updateConsent,
|
|
159
|
+
saveConsents,
|
|
160
|
+
resetConsents
|
|
161
|
+
} = useConsent();
|
|
162
|
+
|
|
163
|
+
// Check if user has given consent for analytics
|
|
164
|
+
if (hasConsented('analytics')) {
|
|
165
|
+
// Initialize analytics
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div>
|
|
170
|
+
{/* Your app content */}
|
|
171
|
+
<button onClick={() => updateConsent('analytics', true)}>Enable Analytics</button>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}`}</code></pre>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
|
179
|
+
<h3 className="text-xl font-bold mb-2">ConsentStorage</h3>
|
|
180
|
+
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
|
181
|
+
A component for handling the storage and retrieval of consent settings. Supports both local storage and custom storage mechanisms.
|
|
182
|
+
</p>
|
|
183
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
184
|
+
<pre><code>{`import { ConsentStorage, ConsentSettings } from '@tantainnovative/ndpr-toolkit';
|
|
185
|
+
import { useState } from 'react';
|
|
186
|
+
|
|
187
|
+
function ConsentStorageExample() {
|
|
188
|
+
const [settings, setSettings] = useState<ConsentSettings>({
|
|
189
|
+
necessary: true,
|
|
190
|
+
analytics: false,
|
|
191
|
+
marketing: false,
|
|
192
|
+
lastUpdated: Date.now()
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const handleLoad = (loadedSettings: ConsentSettings | null) => {
|
|
196
|
+
if (loadedSettings) {
|
|
197
|
+
setSettings(loadedSettings);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<ConsentStorage
|
|
203
|
+
settings={settings}
|
|
204
|
+
storageOptions={{
|
|
205
|
+
key: 'my-app-consent',
|
|
206
|
+
storage: 'localStorage' // or 'sessionStorage' or 'cookie'
|
|
207
|
+
}}
|
|
208
|
+
onLoad={handleLoad}
|
|
209
|
+
onSave={(savedSettings) => console.log('Saved:', savedSettings)}
|
|
210
|
+
autoLoad={true}
|
|
211
|
+
autoSave={true}
|
|
212
|
+
>
|
|
213
|
+
{/* Render prop pattern */}
|
|
214
|
+
{({ loadSettings, saveSettings, clearSettings, loaded }) => (
|
|
215
|
+
<div>
|
|
216
|
+
<p>Consent settings loaded: {loaded ? 'Yes' : 'No'}</p>
|
|
217
|
+
<button onClick={() => saveSettings(settings)}>Save Settings</button>
|
|
218
|
+
<button onClick={() => clearSettings()}>Clear Settings</button>
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
221
|
+
</ConsentStorage>
|
|
222
|
+
);
|
|
223
|
+
}`}</code></pre>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</section>
|
|
228
|
+
|
|
229
|
+
<section id="usage" className="mb-8">
|
|
230
|
+
<h2 className="text-2xl font-bold mb-4">Usage</h2>
|
|
231
|
+
<p className="mb-4">
|
|
232
|
+
Here's a complete example of how to implement the consent management system in your application:
|
|
233
|
+
</p>
|
|
234
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
235
|
+
<pre><code>{`import { useState, useEffect } from 'react';
|
|
236
|
+
import {
|
|
237
|
+
ConsentBanner,
|
|
238
|
+
ConsentPreferences,
|
|
239
|
+
ConsentManager,
|
|
240
|
+
ConsentStorage,
|
|
241
|
+
useConsent
|
|
242
|
+
} from '@tantainnovative/ndpr-toolkit';
|
|
243
|
+
|
|
244
|
+
// Define your consent options
|
|
245
|
+
const consentOptions = [
|
|
246
|
+
{
|
|
247
|
+
id: 'necessary',
|
|
248
|
+
label: 'Necessary Cookies',
|
|
249
|
+
description: 'Essential cookies for the website to function.',
|
|
250
|
+
required: true
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'analytics',
|
|
254
|
+
label: 'Analytics Cookies',
|
|
255
|
+
description: 'Cookies that help us understand how you use our website.',
|
|
256
|
+
required: false
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'marketing',
|
|
260
|
+
label: 'Marketing Cookies',
|
|
261
|
+
description: 'Cookies used for marketing purposes.',
|
|
262
|
+
required: false
|
|
263
|
+
}
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
function App() {
|
|
267
|
+
const [showPreferences, setShowPreferences] = useState(false);
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<ConsentManager
|
|
271
|
+
options={consentOptions}
|
|
272
|
+
storageKey="my-app-consent"
|
|
273
|
+
>
|
|
274
|
+
<div>
|
|
275
|
+
{/* Your app content */}
|
|
276
|
+
<header>
|
|
277
|
+
<nav>
|
|
278
|
+
{/* ... */}
|
|
279
|
+
<button onClick={() => setShowPreferences(true)}>
|
|
280
|
+
Cookie Preferences
|
|
281
|
+
</button>
|
|
282
|
+
</nav>
|
|
283
|
+
</header>
|
|
284
|
+
|
|
285
|
+
<main>
|
|
286
|
+
{/* Your main content */}
|
|
287
|
+
</main>
|
|
288
|
+
|
|
289
|
+
{/* Cookie preferences modal */}
|
|
290
|
+
{showPreferences && (
|
|
291
|
+
<PreferencesModal
|
|
292
|
+
onClose={() => setShowPreferences(false)}
|
|
293
|
+
/>
|
|
294
|
+
)}
|
|
295
|
+
|
|
296
|
+
{/* Consent banner */}
|
|
297
|
+
<ConsentBanner
|
|
298
|
+
options={consentOptions}
|
|
299
|
+
position="bottom"
|
|
300
|
+
privacyPolicyUrl="/privacy-policy"
|
|
301
|
+
/>
|
|
302
|
+
</div>
|
|
303
|
+
</ConsentManager>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function PreferencesModal({ onClose }) {
|
|
308
|
+
const {
|
|
309
|
+
consents,
|
|
310
|
+
updateConsent,
|
|
311
|
+
saveConsents,
|
|
312
|
+
resetConsents
|
|
313
|
+
} = useConsent();
|
|
314
|
+
|
|
315
|
+
const handleSave = () => {
|
|
316
|
+
saveConsents();
|
|
317
|
+
onClose();
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
return (
|
|
321
|
+
<div className="modal">
|
|
322
|
+
<div className="modal-content">
|
|
323
|
+
<h2>Cookie Preferences</h2>
|
|
324
|
+
<ConsentPreferences
|
|
325
|
+
options={consentOptions}
|
|
326
|
+
currentConsent={consents}
|
|
327
|
+
onSave={handleSave}
|
|
328
|
+
onReset={resetConsents}
|
|
329
|
+
/>
|
|
330
|
+
<button onClick={onClose}>Cancel</button>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Example of using the useConsent hook to check consent
|
|
337
|
+
function AnalyticsComponent() {
|
|
338
|
+
const { hasConsented } = useConsent();
|
|
339
|
+
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (hasConsented('analytics')) {
|
|
342
|
+
// Initialize analytics
|
|
343
|
+
console.log('Analytics initialized');
|
|
344
|
+
}
|
|
345
|
+
}, [hasConsented]);
|
|
346
|
+
|
|
347
|
+
return null;
|
|
348
|
+
}`}</code></pre>
|
|
349
|
+
</div>
|
|
350
|
+
</section>
|
|
351
|
+
|
|
352
|
+
<section id="api" className="mb-8">
|
|
353
|
+
<h2 className="text-2xl font-bold mb-4">API Reference</h2>
|
|
354
|
+
|
|
355
|
+
<h3 className="text-xl font-bold mt-8 mb-4">ConsentBanner Props</h3>
|
|
356
|
+
<div className="overflow-x-auto">
|
|
357
|
+
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
358
|
+
<thead className="bg-gray-50 dark:bg-gray-800">
|
|
359
|
+
<tr>
|
|
360
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Prop</th>
|
|
361
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
|
362
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Default</th>
|
|
363
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Description</th>
|
|
364
|
+
</tr>
|
|
365
|
+
</thead>
|
|
366
|
+
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
|
367
|
+
<tr>
|
|
368
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">options</td>
|
|
369
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">ConsentOption[]</td>
|
|
370
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">Required</td>
|
|
371
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Array of consent options to display</td>
|
|
372
|
+
</tr>
|
|
373
|
+
<tr>
|
|
374
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">onSave</td>
|
|
375
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{`({ consents: Record<string, boolean> }) => void`}</td>
|
|
376
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">Required</td>
|
|
377
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Callback function called when consent is saved</td>
|
|
378
|
+
</tr>
|
|
379
|
+
<tr>
|
|
380
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">position</td>
|
|
381
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">'top' | 'bottom'</td>
|
|
382
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">'bottom'</td>
|
|
383
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Position of the banner on the page</td>
|
|
384
|
+
</tr>
|
|
385
|
+
<tr>
|
|
386
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">title</td>
|
|
387
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">string</td>
|
|
388
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">'Cookie Consent'</td>
|
|
389
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Title displayed on the banner</td>
|
|
390
|
+
</tr>
|
|
391
|
+
<tr>
|
|
392
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">description</td>
|
|
393
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">string</td>
|
|
394
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">'We use cookies...'</td>
|
|
395
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Description text explaining the purpose of cookies</td>
|
|
396
|
+
</tr>
|
|
397
|
+
<tr>
|
|
398
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">showPreferences</td>
|
|
399
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">boolean</td>
|
|
400
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">true</td>
|
|
401
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Whether to show the "Preferences" button</td>
|
|
402
|
+
</tr>
|
|
403
|
+
<tr>
|
|
404
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">privacyPolicyUrl</td>
|
|
405
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">string</td>
|
|
406
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">undefined</td>
|
|
407
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">URL to the privacy policy page</td>
|
|
408
|
+
</tr>
|
|
409
|
+
</tbody>
|
|
410
|
+
</table>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<h3 className="text-xl font-bold mt-8 mb-4">ConsentStorage Props</h3>
|
|
414
|
+
<div className="overflow-x-auto">
|
|
415
|
+
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
416
|
+
<thead className="bg-gray-50 dark:bg-gray-800">
|
|
417
|
+
<tr>
|
|
418
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Prop</th>
|
|
419
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
|
420
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Default</th>
|
|
421
|
+
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Description</th>
|
|
422
|
+
</tr>
|
|
423
|
+
</thead>
|
|
424
|
+
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
|
425
|
+
<tr>
|
|
426
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">settings</td>
|
|
427
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">ConsentSettings</td>
|
|
428
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">Required</td>
|
|
429
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Current consent settings to store</td>
|
|
430
|
+
</tr>
|
|
431
|
+
<tr>
|
|
432
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">storageOptions</td>
|
|
433
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">ConsentStorageOptions</td>
|
|
434
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{`{ storageKey: 'ndpr_consent', storageType: 'localStorage' }`}</td>
|
|
435
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Options for storage mechanism</td>
|
|
436
|
+
</tr>
|
|
437
|
+
<tr>
|
|
438
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">onLoad</td>
|
|
439
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{`(settings: ConsentSettings | null) => void`}</td>
|
|
440
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">undefined</td>
|
|
441
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Callback when settings are loaded</td>
|
|
442
|
+
</tr>
|
|
443
|
+
<tr>
|
|
444
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">onSave</td>
|
|
445
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{`(settings: ConsentSettings) => void`}</td>
|
|
446
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">undefined</td>
|
|
447
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Callback when settings are saved</td>
|
|
448
|
+
</tr>
|
|
449
|
+
<tr>
|
|
450
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">autoLoad</td>
|
|
451
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">boolean</td>
|
|
452
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">true</td>
|
|
453
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Whether to load settings on mount</td>
|
|
454
|
+
</tr>
|
|
455
|
+
<tr>
|
|
456
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">autoSave</td>
|
|
457
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">boolean</td>
|
|
458
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">true</td>
|
|
459
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">Whether to save settings automatically</td>
|
|
460
|
+
</tr>
|
|
461
|
+
<tr>
|
|
462
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">children</td>
|
|
463
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">ReactNode | Function</td>
|
|
464
|
+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">undefined</td>
|
|
465
|
+
<td className="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">React nodes or render prop function</td>
|
|
466
|
+
</tr>
|
|
467
|
+
</tbody>
|
|
468
|
+
</table>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<h3 className="text-xl font-bold mt-8 mb-4">ConsentOption Type</h3>
|
|
472
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
473
|
+
<pre><code>{`type ConsentOption = {
|
|
474
|
+
id: string;
|
|
475
|
+
label: string;
|
|
476
|
+
description: string;
|
|
477
|
+
required?: boolean;
|
|
478
|
+
};`}</code></pre>
|
|
479
|
+
</div>
|
|
480
|
+
|
|
481
|
+
<h3 className="text-xl font-bold mt-8 mb-4">ConsentSettings Type</h3>
|
|
482
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
483
|
+
<pre><code>{`type ConsentSettings = {
|
|
484
|
+
[key: string]: boolean;
|
|
485
|
+
lastUpdated: number;
|
|
486
|
+
};`}</code></pre>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<h3 className="text-xl font-bold mt-8 mb-4">ConsentStorageOptions Type</h3>
|
|
490
|
+
<div className="bg-gray-800 text-gray-200 p-4 rounded-md overflow-x-auto">
|
|
491
|
+
<pre><code>{`type ConsentStorageOptions = {
|
|
492
|
+
key: string;
|
|
493
|
+
storage: 'localStorage' | 'sessionStorage' | 'cookie';
|
|
494
|
+
cookieOptions?: {
|
|
495
|
+
path?: string;
|
|
496
|
+
domain?: string;
|
|
497
|
+
secure?: boolean;
|
|
498
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
499
|
+
maxAge?: number;
|
|
500
|
+
};
|
|
501
|
+
};`}</code></pre>
|
|
502
|
+
</div>
|
|
503
|
+
</section>
|
|
504
|
+
|
|
505
|
+
<section id="best-practices" className="mb-8">
|
|
506
|
+
<h2 className="text-2xl font-bold mb-4">Best Practices</h2>
|
|
507
|
+
<ul className="list-disc pl-6 space-y-2">
|
|
508
|
+
<li>
|
|
509
|
+
<strong>Clear Language:</strong> Use clear, plain language to explain what each type of cookie does and why you're collecting the data.
|
|
510
|
+
</li>
|
|
511
|
+
<li>
|
|
512
|
+
<strong>No Pre-selected Options:</strong> Don't pre-select non-essential cookies. The NDPR requires that consent is freely given and pre-selected checkboxes don't constitute valid consent.
|
|
513
|
+
</li>
|
|
514
|
+
<li>
|
|
515
|
+
<strong>Easy Access to Preferences:</strong> Make it easy for users to access and update their consent preferences at any time.
|
|
516
|
+
</li>
|
|
517
|
+
<li>
|
|
518
|
+
<strong>Consent Records:</strong> Keep records of when and how consent was obtained. The ConsentManager component automatically tracks consent history.
|
|
519
|
+
</li>
|
|
520
|
+
<li>
|
|
521
|
+
<strong>Respect User Choices:</strong> Only activate cookies and tracking technologies when the user has given explicit consent for them.
|
|
522
|
+
</li>
|
|
523
|
+
</ul>
|
|
524
|
+
</section>
|
|
525
|
+
|
|
526
|
+
<section id="accessibility" className="mb-8">
|
|
527
|
+
<h2 className="text-2xl font-bold mb-4">Accessibility</h2>
|
|
528
|
+
<p className="mb-4">
|
|
529
|
+
The Consent Management components are built with accessibility in mind:
|
|
530
|
+
</p>
|
|
531
|
+
<ul className="list-disc pl-6 space-y-2">
|
|
532
|
+
<li>All form elements have proper labels and ARIA attributes</li>
|
|
533
|
+
<li>Focus states are clearly visible</li>
|
|
534
|
+
<li>Color contrast meets WCAG 2.1 AA standards</li>
|
|
535
|
+
<li>Keyboard navigation is fully supported</li>
|
|
536
|
+
<li>The banner is announced to screen readers when it appears</li>
|
|
537
|
+
</ul>
|
|
538
|
+
</section>
|
|
539
|
+
|
|
540
|
+
<section id="help-resources" className="mb-8">
|
|
541
|
+
<h2 className="text-2xl font-bold mb-4">Need Help?</h2>
|
|
542
|
+
<p className="mb-4">
|
|
543
|
+
If you have questions about implementing the Consent Management system or need assistance with NDPR compliance, check out these resources:
|
|
544
|
+
</p>
|
|
545
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
546
|
+
<Card>
|
|
547
|
+
<CardContent className="p-4">
|
|
548
|
+
<h3 className="font-medium text-gray-900 dark:text-white mb-2">GitHub Issues</h3>
|
|
549
|
+
<p className="text-gray-600 dark:text-gray-300 text-sm mb-3">
|
|
550
|
+
Report bugs or request features on our GitHub repository.
|
|
551
|
+
</p>
|
|
552
|
+
<Button asChild variant="outline" size="sm">
|
|
553
|
+
<a href="https://github.com/tantainnovative/ndpr-toolkit/issues" target="_blank" rel="noopener noreferrer">
|
|
554
|
+
View Issues
|
|
555
|
+
</a>
|
|
556
|
+
</Button>
|
|
557
|
+
</CardContent>
|
|
558
|
+
</Card>
|
|
559
|
+
<Card>
|
|
560
|
+
<CardContent className="p-4">
|
|
561
|
+
<h3 className="font-medium text-gray-900 dark:text-white mb-2">NDPR Resources</h3>
|
|
562
|
+
<p className="text-gray-600 dark:text-gray-300 text-sm mb-3">
|
|
563
|
+
Learn more about NDPR compliance requirements.
|
|
564
|
+
</p>
|
|
565
|
+
<Button asChild variant="outline" size="sm">
|
|
566
|
+
<a href="https://nitda.gov.ng/wp-content/uploads/2020/01/NDPR-Implementation-Framework.pdf" target="_blank" rel="noopener noreferrer">
|
|
567
|
+
NDPR Framework
|
|
568
|
+
</a>
|
|
569
|
+
</Button>
|
|
570
|
+
</CardContent>
|
|
571
|
+
</Card>
|
|
572
|
+
</div>
|
|
573
|
+
</section>
|
|
574
|
+
</DocLayout>
|
|
575
|
+
);
|
|
576
|
+
}
|