@tantainnovative/ndpr-toolkit 1.0.1 → 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.
Files changed (212) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/.eslintrc.json +10 -0
  3. package/.github/workflows/ci.yml +36 -0
  4. package/.github/workflows/nextjs.yml +104 -0
  5. package/.husky/commit-msg +4 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.lintstagedrc.js +4 -0
  8. package/.nvmrc +1 -0
  9. package/.versionrc +17 -0
  10. package/CHANGELOG.md +16 -0
  11. package/CLAUDE.md +90 -0
  12. package/CNAME +1 -0
  13. package/CONTRIBUTING.md +87 -0
  14. package/README.md +84 -431
  15. package/RELEASE-NOTES-v1.0.0.md +140 -0
  16. package/RELEASE-NOTES-v1.0.1.md +69 -0
  17. package/SECURITY.md +21 -0
  18. package/commitlint.config.js +36 -0
  19. package/components.json +21 -0
  20. package/eslint.config.mjs +16 -0
  21. package/jest.config.js +31 -0
  22. package/jest.setup.js +15 -0
  23. package/next.config.js +15 -0
  24. package/next.config.ts +62 -0
  25. package/package.json +70 -52
  26. package/packages/ndpr-toolkit/README.md +467 -0
  27. package/packages/ndpr-toolkit/jest.config.js +23 -0
  28. package/packages/ndpr-toolkit/package-lock.json +8197 -0
  29. package/packages/ndpr-toolkit/package.json +71 -0
  30. package/packages/ndpr-toolkit/rollup.config.js +34 -0
  31. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
  32. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
  33. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
  34. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
  35. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
  36. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
  37. package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
  38. package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
  39. package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
  40. package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
  41. package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
  42. package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
  43. package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
  44. package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
  45. package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
  46. package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
  47. package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
  48. package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
  49. package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
  50. package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
  51. package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
  52. package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
  53. package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
  54. package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
  55. package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
  56. package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
  57. package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
  58. package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
  59. package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
  60. package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
  61. package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
  62. package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
  63. package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
  64. package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
  65. package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +14 -1
  66. package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
  67. package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
  68. package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
  69. package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
  70. package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
  71. package/packages/ndpr-toolkit/src/types/index.ts +42 -0
  72. package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
  73. package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
  74. package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
  75. package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
  76. package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
  77. package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
  78. package/packages/ndpr-toolkit/tsconfig.json +23 -0
  79. package/postcss.config.mjs +5 -0
  80. package/public/NDPR TOOLKIT.svg +1 -0
  81. package/public/favicon/android-chrome-192x192.png +0 -0
  82. package/public/favicon/android-chrome-512x512.png +0 -0
  83. package/public/favicon/apple-touch-icon.png +0 -0
  84. package/public/favicon/favicon-16x16.png +0 -0
  85. package/public/favicon/favicon-32x32.png +0 -0
  86. package/public/favicon/site.webmanifest +1 -0
  87. package/public/file.svg +1 -0
  88. package/public/globe.svg +1 -0
  89. package/public/ndpr-toolkit-logo.svg +108 -0
  90. package/public/next.svg +1 -0
  91. package/public/vercel.svg +1 -0
  92. package/public/window.svg +1 -0
  93. package/src/__tests__/example.test.ts +13 -0
  94. package/src/__tests__/requestService.test.ts +57 -0
  95. package/src/app/accessibility.css +70 -0
  96. package/src/app/docs/components/DocLayout.tsx +267 -0
  97. package/src/app/docs/components/breach-notification/page.tsx +797 -0
  98. package/src/app/docs/components/consent-management/page.tsx +576 -0
  99. package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
  100. package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
  101. package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
  102. package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
  103. package/src/app/docs/components/hooks/page.tsx +305 -0
  104. package/src/app/docs/components/page.tsx +84 -0
  105. package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
  106. package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
  107. package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
  108. package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
  109. package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
  110. package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
  111. package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
  112. package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
  113. package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
  114. package/src/app/docs/guides/managing-consent/page.tsx +738 -0
  115. package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
  116. package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
  117. package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
  118. package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
  119. package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
  120. package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
  121. package/src/app/docs/guides/page.tsx +67 -0
  122. package/src/app/docs/layout.tsx +15 -0
  123. package/src/app/docs/metadata.ts +31 -0
  124. package/src/app/docs/page.tsx +572 -0
  125. package/src/app/favicon.ico +0 -0
  126. package/src/app/globals.css +123 -0
  127. package/src/app/layout.tsx +37 -0
  128. package/src/app/ndpr-demos/breach/page.tsx +354 -0
  129. package/src/app/ndpr-demos/consent/page.tsx +366 -0
  130. package/src/app/ndpr-demos/dpia/page.tsx +495 -0
  131. package/src/app/ndpr-demos/dsr/page.tsx +280 -0
  132. package/src/app/ndpr-demos/page.tsx +73 -0
  133. package/src/app/ndpr-demos/policy/page.tsx +771 -0
  134. package/src/app/page.tsx +452 -0
  135. package/src/components/ErrorBoundary.tsx +90 -0
  136. package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
  137. package/src/components/consent/ConsentBanner.tsx +159 -0
  138. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
  139. package/src/components/docs/DocLayout.tsx +289 -0
  140. package/src/components/docs/index.ts +2 -0
  141. package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
  142. package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
  143. package/src/components/privacy-policy/data.ts +98 -0
  144. package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
  145. package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
  146. package/src/components/privacy-policy/shared/FormField.tsx +79 -0
  147. package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
  148. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
  149. package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
  150. package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
  151. package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
  152. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
  153. package/src/components/ui/Badge.tsx +46 -0
  154. package/src/components/ui/Button.tsx +59 -0
  155. package/src/components/ui/Card.tsx +92 -0
  156. package/src/components/ui/Checkbox.tsx +57 -0
  157. package/src/components/ui/FormField.tsx +50 -0
  158. package/src/components/ui/Input.tsx +38 -0
  159. package/src/components/ui/Loading.tsx +201 -0
  160. package/src/components/ui/Select.tsx +42 -0
  161. package/src/components/ui/TextArea.tsx +38 -0
  162. package/src/components/ui/label.tsx +24 -0
  163. package/src/components/ui/switch.tsx +31 -0
  164. package/src/components/ui/tabs.tsx +66 -0
  165. package/src/hooks/useConsent.ts +64 -0
  166. package/src/hooks/useLoadingState.ts +85 -0
  167. package/src/lib/consentService.ts +137 -0
  168. package/src/lib/dpiaQuestions.ts +148 -0
  169. package/src/lib/requestService.ts +75 -0
  170. package/src/lib/sanitize.ts +108 -0
  171. package/src/lib/storage.ts +222 -0
  172. package/src/lib/utils.ts +6 -0
  173. package/src/types/html-to-docx.d.ts +30 -0
  174. package/src/types/index.ts +72 -0
  175. package/tailwind.config.ts +65 -0
  176. package/tsconfig.json +41 -0
  177. package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
  178. package/dist/components/breach/BreachReportForm.d.ts +0 -66
  179. package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
  180. package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
  181. package/dist/components/consent/ConsentBanner.d.ts +0 -79
  182. package/dist/components/consent/ConsentManager.d.ts +0 -73
  183. package/dist/components/consent/ConsentStorage.d.ts +0 -41
  184. package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
  185. package/dist/components/dpia/DPIAReport.d.ts +0 -40
  186. package/dist/components/dpia/StepIndicator.d.ts +0 -64
  187. package/dist/components/dsr/DSRDashboard.d.ts +0 -58
  188. package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
  189. package/dist/components/dsr/DSRTracker.d.ts +0 -56
  190. package/dist/components/policy/PolicyExporter.d.ts +0 -65
  191. package/dist/components/policy/PolicyGenerator.d.ts +0 -54
  192. package/dist/components/policy/PolicyPreview.d.ts +0 -71
  193. package/dist/hooks/useBreach.d.ts +0 -97
  194. package/dist/hooks/useConsent.d.ts +0 -63
  195. package/dist/hooks/useDPIA.d.ts +0 -92
  196. package/dist/hooks/useDSR.d.ts +0 -72
  197. package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
  198. package/dist/index.esm.js +0 -2
  199. package/dist/index.esm.js.map +0 -1
  200. package/dist/index.js +0 -2
  201. package/dist/index.js.map +0 -1
  202. package/dist/setupTests.d.ts +0 -2
  203. package/dist/types/breach.d.ts +0 -239
  204. package/dist/types/consent.d.ts +0 -95
  205. package/dist/types/dpia.d.ts +0 -196
  206. package/dist/types/dsr.d.ts +0 -162
  207. package/dist/types/privacy.d.ts +0 -204
  208. package/dist/utils/breach.d.ts +0 -14
  209. package/dist/utils/consent.d.ts +0 -10
  210. package/dist/utils/dpia.d.ts +0 -12
  211. package/dist/utils/dsr.d.ts +0 -11
  212. package/dist/utils/privacy.d.ts +0 -12
@@ -0,0 +1,771 @@
1
+ "use client";
2
+
3
+ import React, { useState } from "react";
4
+ import Link from "next/link";
5
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle,
12
+ } from "@/components/ui/Card";
13
+ import {
14
+ PolicyGenerator,
15
+ PolicyPreview,
16
+ PolicyExporter,
17
+ PolicySection,
18
+ PolicyVariable,
19
+ } from "@tantainnovative/ndpr-toolkit";
20
+ import type { PolicyTemplate } from "@tantainnovative/ndpr-toolkit";
21
+
22
+ export default function PolicyDemoPage() {
23
+ const [activeTab, setActiveTab] = useState<string>("generator");
24
+ // Initialize with empty objects/arrays to prevent undefined errors
25
+ const [policyData, setPolicyData] = useState<Record<string, unknown>>({});
26
+ const [generatedPolicy, setGeneratedPolicy] = useState<PolicySection[]>([]);
27
+ const [policyVariables, setPolicyVariables] = useState<PolicyVariable[]>([]);
28
+
29
+ // Helper: process conditional {{#if …}}…{{else}}…{{/if}} blocks
30
+ const processConditionalBlocks = (
31
+ content: string,
32
+ data: Record<string, unknown>,
33
+ ): string => {
34
+ if (!content || typeof content !== "string") return "";
35
+ if (!data || typeof data !== "object") data = {};
36
+
37
+ try {
38
+ // First pass: Process nested if blocks from innermost to outermost
39
+ let processedContent = content;
40
+ let lastContent = "";
41
+
42
+ // Keep processing until no more changes are made (handles nested conditionals)
43
+ while (processedContent !== lastContent) {
44
+ lastContent = processedContent;
45
+ const ifRegex =
46
+ /\{\{#if ([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g;
47
+
48
+ processedContent = processedContent.replace(
49
+ ifRegex,
50
+ (_match, variable, ifContent, elseContent = "") => {
51
+ if (!variable || typeof variable !== "string") return elseContent;
52
+
53
+ // Handle complex conditions with AND/OR operators
54
+ if (variable.includes("&&") || variable.includes("||")) {
55
+ try {
56
+ // Create a safe evaluation context with data variables
57
+ const evalContext = { ...data };
58
+ // Replace operators with JavaScript operators
59
+ const jsCondition = variable
60
+ .replace(/\s*&&\s*/g, " && ")
61
+ .replace(/\s*\|\|\s*/g, " || ");
62
+
63
+ // Safely evaluate the condition
64
+ const result = Object.keys(evalContext).some(
65
+ (key) => jsCondition.includes(key) && evalContext[key],
66
+ );
67
+
68
+ return result ? ifContent : elseContent;
69
+ } catch (error) {
70
+ console.error("Error evaluating complex condition:", error);
71
+ return elseContent;
72
+ }
73
+ }
74
+
75
+ // Handle simple conditions
76
+ let value = data[variable];
77
+ if (value === "true") value = true;
78
+ if (value === "false") value = false;
79
+ if (Array.isArray(value) && value.length === 0) value = false;
80
+ if (value === "" || value === undefined || value === null)
81
+ value = false;
82
+
83
+ return value ? ifContent : elseContent;
84
+ },
85
+ );
86
+ }
87
+
88
+ // Second pass: Clean up any empty lines and extra whitespace
89
+ return processedContent
90
+ .replace(/\n{3,}/g, "\n\n") // Replace multiple newlines with double newlines
91
+ .replace(/\s+\n/g, "\n") // Remove trailing whitespace
92
+ .trim();
93
+ } catch (error) {
94
+ console.error("Error processing conditional blocks:", error);
95
+ return content; // Return original content if tHere&apos;s an error
96
+ }
97
+ };
98
+
99
+ // Build the exportable markdown/HTML content
100
+ const generateFormattedContent = (): string => {
101
+ try {
102
+ if (
103
+ !generatedPolicy ||
104
+ !Array.isArray(generatedPolicy) ||
105
+ generatedPolicy.length === 0
106
+ ) {
107
+ return "# No policy content generated yet\n\nPlease use the generator to create your policy.";
108
+ }
109
+
110
+ // Get current date in a professional format
111
+ const formattedDate = new Date().toLocaleDateString("en-US", {
112
+ year: "numeric",
113
+ month: "long",
114
+ day: "numeric",
115
+ });
116
+
117
+ // Create a professional header with proper spacing and formatting
118
+ const policyTitle = `# ${policyData?.organizationName || "Your Organization"} Privacy Policy\n\n`;
119
+ const lastUpdated = `*Last Updated: ${formattedDate}*\n\n`;
120
+ const complianceNotice = `*This privacy policy is designed to comply with the Nigeria Data Protection Regulation (NDPR) and has been prepared by ${policyData?.organizationName || "Your Organization"}.*\n\n`;
121
+
122
+ // Add a professional table of contents
123
+ let tableOfContents = "## Table of Contents\n\n";
124
+ const includedSections = generatedPolicy.filter(
125
+ (section) => section && section.included !== false,
126
+ );
127
+
128
+ includedSections.forEach((section, index) => {
129
+ if (section && section.title) {
130
+ tableOfContents += `${index + 1}. [${section.title}](#${section.id})\n`;
131
+ }
132
+ });
133
+ tableOfContents += "\n";
134
+
135
+ // Process each section with careful error handling and enhanced formatting
136
+ const sectionsContent = includedSections
137
+ .map((section, sectionIndex) => {
138
+ try {
139
+ if (
140
+ !section ||
141
+ !section.template ||
142
+ typeof section.template !== "string"
143
+ ) {
144
+ return "";
145
+ }
146
+
147
+ let processed = section.template;
148
+ const sectionVars = section.variables || [];
149
+
150
+ // Process all variables for this section
151
+ if (Array.isArray(sectionVars)) {
152
+ sectionVars.forEach((varName) => {
153
+ if (typeof varName !== "string") return;
154
+
155
+ let val: unknown = policyData?.[varName] ?? "";
156
+
157
+ // Format array values as professional bullet points
158
+ if (Array.isArray(val) && val.length > 0) {
159
+ val = val.map((item, i) => `${i + 1}. ${item}`).join("\n\n");
160
+ } else if (Array.isArray(val)) {
161
+ val = "Not specified";
162
+ }
163
+
164
+ // Handle special formatting for dates
165
+ if (
166
+ varName.toLowerCase().includes("date") &&
167
+ typeof val === "string"
168
+ ) {
169
+ try {
170
+ const date = new Date(val);
171
+ if (!isNaN(date.getTime())) {
172
+ val = date.toLocaleDateString("en-US", {
173
+ year: "numeric",
174
+ month: "long",
175
+ day: "numeric",
176
+ });
177
+ }
178
+ } catch {
179
+ // Keep original value if date parsing fails
180
+ }
181
+ }
182
+
183
+ try {
184
+ // Replace both triple and double braces for HTML/plain text
185
+ const valStr = String(val);
186
+ processed = processed
187
+ .replace(
188
+ new RegExp(`\\{\\{\\{${varName}\\}\\}\\}`, "g"),
189
+ valStr,
190
+ )
191
+ .replace(new RegExp(`\\{\\{${varName}\\}\\}`, "g"), valStr);
192
+ } catch (regexError) {
193
+ console.error("Error replacing variables:", regexError);
194
+ }
195
+ });
196
+ }
197
+
198
+ // Process conditional blocks with enhanced error handling
199
+ try {
200
+ processed = processConditionalBlocks(processed, policyData || {});
201
+ } catch (condError) {
202
+ console.error("Error processing conditional blocks:", condError);
203
+ }
204
+
205
+ // Add section number for better organization
206
+ return `## ${sectionIndex + 1}. ${section.title || "Untitled Section"} {#${section.id}}\n\n${processed}`;
207
+ } catch (sectionError) {
208
+ console.error("Error processing section:", sectionError);
209
+ return "";
210
+ }
211
+ })
212
+ .filter(Boolean)
213
+ .join("\n\n");
214
+
215
+ // Add a professional footer
216
+ const footer = `\n\n---\n\n*This privacy policy was last updated on ${formattedDate}. If you have any questions about this policy, please contact ${policyData?.dpoName || "our Data Protection Officer"} at ${policyData?.dpoEmail || policyData?.contactEmail || "our contact email"}.*\n\n© ${new Date().getFullYear()} ${policyData?.organizationName || "Your Organization"}. All rights reserved.`;
217
+
218
+ return (
219
+ policyTitle +
220
+ lastUpdated +
221
+ complianceNotice +
222
+ tableOfContents +
223
+ sectionsContent +
224
+ footer
225
+ );
226
+ } catch (error) {
227
+ console.error("Error generating formatted content:", error);
228
+ return "# Error Generating Policy\n\nThere was an error generating your policy content. Please try again.";
229
+ }
230
+ };
231
+
232
+ // Called when PolicyGenerator emits data
233
+ const handleGeneratePolicy = (data: Record<string, unknown>) => {
234
+ try {
235
+ console.log("Policy generator data received:", data);
236
+
237
+ // Validate sections
238
+ const sections = Array.isArray(data?.sections) ? data.sections : [];
239
+ // Validate variables
240
+ const variables = Array.isArray(data?.variables) ? data.variables : [];
241
+ // Validate values
242
+ const values =
243
+ data?.values && typeof data.values === "object" ? data.values : {};
244
+
245
+ // Update state with validated data
246
+ setGeneratedPolicy(sections);
247
+ setPolicyVariables(variables);
248
+ setPolicyData(values as Record<string, unknown>);
249
+
250
+ // Only navigate if we have valid sections
251
+ if (sections.length > 0) {
252
+ setActiveTab("display");
253
+ } else {
254
+ console.warn("No policy sections were generated");
255
+ }
256
+ } catch (error) {
257
+ console.error("Error handling policy generation:", error);
258
+ // Initialize with empty arrays to prevent mapping errors
259
+ setGeneratedPolicy([]);
260
+ setPolicyVariables([]);
261
+ setPolicyData({});
262
+ }
263
+ };
264
+
265
+ // Handle policy edits - we'll need to implement this differently since PolicyPreview doesn&apos;t support direct section updates
266
+ const handlePolicyEdit = () => {
267
+ console.log("Policy edit requested");
268
+ // In a real implementation, you might want to switch back to the generator tab or open an edit modal
269
+ setActiveTab("generator");
270
+ };
271
+
272
+ // Your NDPR template definition
273
+ const policyTemplate: PolicyTemplate = {
274
+ id: "ndpr-policy",
275
+ name: "NDPR Compliant Privacy Policy",
276
+ description: "A comprehensive privacy policy template compliant with NDPR",
277
+ organizationType: "business",
278
+ version: "1.0",
279
+ lastUpdated: Date.now(), // Using timestamp as required by the PolicyTemplate type
280
+ variables: {
281
+ organizationName: {
282
+ name: "Organization Name",
283
+ description: "The name of your organization",
284
+ required: true,
285
+ defaultValue: "Your Company",
286
+ },
287
+ website: {
288
+ name: "Website URL",
289
+ description: "Your organization&apos;s website",
290
+ required: true,
291
+ defaultValue: "https://example.com",
292
+ },
293
+ contactEmail: {
294
+ name: "Contact Email",
295
+ description: "Email for privacy inquiries",
296
+ required: true,
297
+ defaultValue: "privacy@example.com",
298
+ },
299
+ address: {
300
+ name: "Business Address",
301
+ description: "Physical address of your organization",
302
+ required: true,
303
+ defaultValue: "123 Business Street, Lagos, Nigeria",
304
+ },
305
+ phone: {
306
+ name: "Contact Phone",
307
+ description: "Phone number for privacy inquiries",
308
+ required: true,
309
+ defaultValue: "+234 123 456 7890",
310
+ },
311
+ dpoName: {
312
+ name: "Data Protection Officer Name",
313
+ description: "Name of your Data Protection Officer",
314
+ required: true,
315
+ defaultValue: "John Doe",
316
+ },
317
+ dpoEmail: {
318
+ name: "DPO Email",
319
+ description: "Email of your Data Protection Officer",
320
+ required: true,
321
+ defaultValue: "dpo@example.com",
322
+ },
323
+ industry: {
324
+ name: "Industry",
325
+ description: "Your organization&apos;s industry or sector",
326
+ required: true,
327
+ defaultValue: "Technology",
328
+ },
329
+ effectiveDate: {
330
+ name: "Effective Date",
331
+ description: "When this privacy policy becomes effective",
332
+ required: true,
333
+ defaultValue: new Date().toLocaleDateString(),
334
+ },
335
+ },
336
+ sections: [
337
+ {
338
+ id: "introduction",
339
+ title: "Introduction",
340
+ required: true,
341
+ description:
342
+ "Introduce your organization and the purpose of the policy",
343
+ template: `## Introduction
344
+
345
+ {{organizationName}} ("we", "us", or "our") is committed to protecting your personal data and respecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you interact with our services, website, and applications.
346
+
347
+ This policy is compliant with the Nigeria Data Protection Regulation (NDPR) 2019 and applies to all personal data processed by us. By using our services, you consent to the data practices described in this Privacy Policy.
348
+
349
+ ### About Us
350
+
351
+ {{organizationName}} is a {{organizationType}} organization registered in Nigeria with its principal place of business at {{address}}. We can be contacted at:
352
+
353
+ - Email: {{contactEmail}}
354
+ - Phone: {{phone}}
355
+ - Address: {{address}}
356
+
357
+ ### Data Protection Officer
358
+
359
+ We have appointed a Data Protection Officer ("DPO") who is responsible for overseeing questions regarding this Privacy Policy. If you have any questions about this Privacy Policy, including any requests to exercise your legal rights, please contact our DPO using the details below:
360
+
361
+ - Name: {{dpoName}}
362
+ - Email: {{dpoEmail}}
363
+ - Address: {{address}}
364
+
365
+ ### Effective Date
366
+
367
+ This Privacy Policy is effective as of {{effectiveDate}} and will remain in effect except with respect to any changes in its provisions in the future, which will be in effect immediately after being posted on this page.`,
368
+ included: true,
369
+ variables: [
370
+ "organizationName",
371
+ "organizationType",
372
+ "address",
373
+ "contactEmail",
374
+ "phone",
375
+ "dpoName",
376
+ "dpoEmail",
377
+ "effectiveDate",
378
+ ],
379
+ },
380
+ {
381
+ id: "definitions",
382
+ title: "Definitions",
383
+ required: true,
384
+ description: "Define key terms used throughout the policy",
385
+ template: `## Key Definitions
386
+
387
+ To help you understand this Privacy Policy, here are definitions of key terms used throughout:
388
+
389
+ - **Personal Data**: Any information relating to an identified or identifiable natural person ('data subject'); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person.
390
+
391
+ - **Data Subject**: A natural person who is the subject of Personal Data, i.e., the individual to whom the Personal Data relates.
392
+
393
+ - **Processing**: Any operation or set of operations which is performed on Personal Data or on sets of Personal Data, whether or not by automated means, such as collection, recording, organization, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure or destruction.
394
+
395
+ - **Data Controller**: The natural or legal person, public authority, agency or other body which, alone or jointly with others, determines the purposes and means of the processing of Personal Data. For the purposes of this Privacy Policy, {{organizationName}} is the Data Controller.
396
+
397
+ - **Data Processor**: A natural or legal person, public authority, agency or other body which processes Personal Data on behalf of the Data Controller.
398
+
399
+ - **Consent**: Any freely given, specific, informed and unambiguous indication of the Data Subject's wishes by which he or she, by a statement or by a clear affirmative action, signifies agreement to the processing of Personal Data relating to him or her.
400
+
401
+ - **Data Breach**: A breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to, Personal Data transmitted, stored or otherwise processed.
402
+
403
+ - **NDPR**: The Nigeria Data Protection Regulation 2019, issued by the National Information Technology Development Agency (NITDA).
404
+
405
+ - **NITDA**: The National Information Technology Development Agency, the regulatory body responsible for enforcing the NDPR in Nigeria.
406
+
407
+ - **Special Categories of Personal Data**: Personal Data revealing racial or ethnic origin, political opinions, religious or philosophical beliefs, trade union membership, genetic data, biometric data, data concerning health, or data concerning a natural person's sex life or sexual orientation.`,
408
+ included: true,
409
+ variables: ["organizationName"],
410
+ },
411
+ {
412
+ id: "dataCollection",
413
+ title: "Data Collection",
414
+ required: true,
415
+ description:
416
+ "Explain what personal data you collect and how you collect it",
417
+ template: `## Personal Data We Collect
418
+
419
+ ### Categories of Personal Data We Collect
420
+
421
+ {{#if collectsPersonalData}}
422
+ We collect and process various categories of personal data for the purposes set out in this Privacy Policy. These categories include:
423
+
424
+ {{{dataTypes}}}
425
+ {{else}}
426
+ We do not collect personal data beyond what is necessary for basic website functionality.
427
+ {{/if}}
428
+
429
+ ### How We Collect Personal Data
430
+
431
+ We collect personal data through various methods, including:
432
+
433
+ 1. **Direct Interactions**: When you provide your personal data by filling in forms, creating an account, subscribing to our services, requesting marketing materials, participating in surveys, or corresponding with us.
434
+
435
+ 2. **Automated Technologies**: As you interact with our {{website}}, we automatically collect Technical Data about your equipment, browsing actions, and patterns using cookies, server logs, and other similar technologies.
436
+
437
+ 3. **Third Parties or Publicly Available Sources**: We may receive personal data about you from various third parties and public sources, such as analytics providers, advertising networks, search information providers, data brokers, or publicly available databases.
438
+
439
+ ### Cookies and Similar Technologies
440
+
441
+ {{#if usesCookies}}
442
+ We use cookies and similar tracking technologies to track activity on our services and to hold certain information. The types of cookies we use include:
443
+
444
+ {{{cookieTypes}}}
445
+
446
+ You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our services.
447
+ {{else}}
448
+ We do not use cookies or similar tracking technologies on our website.
449
+ {{/if}}
450
+
451
+ ### Children's Privacy
452
+
453
+ {{#if collectsChildrenData}}
454
+ We may collect data from children under 13 with verifiable parental consent. Parents can review, delete, or refuse further collection of their child's personal data by contacting us using the details provided in this Privacy Policy.
455
+ {{else}}
456
+ Our services are not intended for children under the age of 13, and we do not knowingly collect personal data from children under 13. If we learn we have collected or received personal data from a child under 13 without verification of parental consent, we will delete that information.
457
+ {{/if}}`,
458
+ included: true,
459
+ variables: [
460
+ "collectsPersonalData",
461
+ "dataTypes",
462
+ "website",
463
+ "usesCookies",
464
+ "cookieTypes",
465
+ "collectsChildrenData",
466
+ ],
467
+ },
468
+ {
469
+ id: "dataUse",
470
+ title: "Use of Personal Data",
471
+ required: true,
472
+ description: "Explain how you use the personal data you collect",
473
+ template: `## How We Use Your Personal Data
474
+
475
+ ### Purposes for Processing Your Personal Data
476
+
477
+ {{#if collectsPersonalData}}
478
+ We will only use your personal data when the law allows us and for legitimate purposes. We process your personal data for the following purposes:
479
+
480
+ {{{dataPurposes}}}
481
+ {{else}}
482
+ We do not collect or process personal data beyond what is necessary for basic website functionality.
483
+ {{/if}}
484
+
485
+ ### Automated Decision-Making and Profiling
486
+
487
+ We may use automated decision-making processes, including profiling, to analyze your personal data and make decisions that may have a significant effect on you. These processes may be used for purposes such as:
488
+
489
+ - Determining your eligibility for certain products or services
490
+ - Personalizing your experience and the content we show you
491
+ - Detecting fraudulent or suspicious activity
492
+
493
+ You have the right not to be subject to a decision based solely on automated processing, including profiling, which produces legal effects concerning you or similarly significantly affects you, except where:
494
+
495
+ - It is necessary for entering into or performing a contract between you and us
496
+ - It is authorized by applicable law
497
+ - It is based on your explicit consent
498
+
499
+ ### Change of Purpose
500
+
501
+ We will only use your personal data for the purposes for which we collected it, unless we reasonably consider that we need to use it for another reason and that reason is compatible with the original purpose. If we need to use your personal data for an unrelated purpose, we will notify you and explain the legal basis which allows us to do so.
502
+
503
+ Please note that we may process your personal data without your knowledge or consent, in compliance with the above rules, where this is required or permitted by law.`,
504
+ included: true,
505
+ variables: ["collectsPersonalData", "dataPurposes"],
506
+ },
507
+ {
508
+ id: "legalBasis",
509
+ title: "Legal Basis for Processing",
510
+ required: true,
511
+ description: "Explain the legal basis for processing personal data",
512
+ template: `## Legal Basis for Processing Personal Data
513
+
514
+ Under the Nigeria Data Protection Regulation (NDPR), we must have a valid legal basis for processing your personal data. We rely on the following legal bases for processing your personal data:
515
+
516
+ {{#if legalBases}}
517
+ {{{legalBases}}}
518
+ {{else}}
519
+ ### Consent
520
+
521
+ We process certain personal data based on your consent. This means you have given us specific, informed, and unambiguous consent to process your personal data for particular purposes. For example, when you opt-in to receive marketing communications or agree to the use of certain cookies on our website.
522
+
523
+ You have the right to withdraw your consent at any time by contacting us using the details provided in this Privacy Policy or by using the opt-out mechanisms we provide (such as unsubscribe links in our marketing emails).
524
+
525
+ ### Contractual Necessity
526
+
527
+ We process personal data when it is necessary for the performance of a contract to which you are a party or to take steps at your request before entering into such a contract. For example, when you purchase our products or services, we need to process your personal data to fulfill your order, provide customer support, and manage your account.
528
+
529
+ ### Legal Obligation
530
+
531
+ We process personal data when it is necessary for compliance with a legal obligation to which we are subject. For example, we may need to process your personal data to comply with tax laws, regulatory requirements, or court orders.
532
+
533
+ ### Legitimate Interests
534
+
535
+ We process personal data when it is necessary for the purposes of the legitimate interests pursued by us or by a third party, except where such interests are overridden by your interests or fundamental rights and freedoms which require protection of personal data.
536
+ {{/if}}
537
+
538
+ ### Special Categories of Personal Data
539
+
540
+ For special categories of personal data (such as data revealing racial or ethnic origin, political opinions, religious beliefs, health data, or biometric data), we will generally process such data only with your explicit consent, unless the processing is necessary for one of the other legal bases specifically permitted under the NDPR.
541
+ - For the establishment, exercise, or defense of legal claims
542
+ - For reasons of substantial public interest, on the basis of Nigerian law
543
+ - For preventive or occupational medicine, medical diagnosis, or the provision of health or social care`,
544
+ included: true,
545
+ variables: ["legalBases"],
546
+ },
547
+ {
548
+ id: "dataSharing",
549
+ title: "Data Sharing",
550
+ required: true,
551
+ description: "Explain how you share personal data with third parties",
552
+ template: `## Data Sharing
553
+
554
+ ### Third-Party Disclosures
555
+
556
+ {{#if collectsPersonalData}}
557
+ We may share your personal data with the following categories of third parties:
558
+
559
+ {{{dataRecipients}}}
560
+ {{else}}
561
+ We do not share personal data with third parties beyond what is necessary for basic website functionality.
562
+ {{/if}}
563
+
564
+ ### Safeguards for Third-Party Transfers
565
+
566
+ We require all third parties to respect the security of your personal data and to treat it in accordance with the law. We do not allow our third-party service providers to use your personal data for their own purposes and only permit them to process your personal data for specified purposes and in accordance with our instructions.
567
+
568
+ ### International Transfers
569
+
570
+ {{#if transfersDataInternationally}}
571
+ We may transfer your personal data to countries outside Nigeria. When we do, we ensure a similar degree of protection is afforded to your personal data by ensuring at least one of the following safeguards is implemented:
572
+
573
+ - We will only transfer your personal data to countries that have been deemed to provide an adequate level of protection for personal data.
574
+ - Where we use certain service providers, we may use specific contracts approved for use in Nigeria which give personal data the same protection it has in Nigeria.
575
+ - Where we transfer data to the United States or other regions, we may use specific certification mechanisms or obtain your consent for the transfer.
576
+
577
+ Please contact us if you want further information on the specific mechanism used by us when transferring your personal data out of Nigeria.
578
+ {{/if}}`,
579
+ included: true,
580
+ variables: [
581
+ "collectsPersonalData",
582
+ "dataRecipients",
583
+ "transfersDataInternationally",
584
+ ],
585
+ },
586
+ {
587
+ id: "dataRetention",
588
+ title: "Data Retention",
589
+ required: true,
590
+ description: "Explain how long you retain personal data",
591
+ template: `## Data Retention
592
+ ### How Long We Keep Your Personal Data
593
+
594
+ {{#if collectsPersonalData}}
595
+ We will only retain your personal data for as long as necessary to fulfill the purposes we collected it for, including for the purposes of satisfying any legal, accounting, or reporting requirements.
596
+
597
+ Our standard retention period for personal data is: {{dataRetentionPeriod}}
598
+
599
+ To determine the appropriate retention period for personal data, we consider the amount, nature, and sensitivity of the personal data, the potential risk of harm from unauthorized use or disclosure of your personal data, the purposes for which we process your personal data and whether we can achieve those purposes through other means, and the applicable legal requirements.
600
+ {{else}}
601
+ We do not retain personal data beyond what is necessary for basic website functionality.
602
+ {{/if}}
603
+
604
+ ### Data Deletion
605
+
606
+ When your personal data is no longer required for the purposes for which it was collected, we will delete or anonymize it. If this is not possible (for example, because your personal data has been stored in backup archives), then we will securely store your personal data and isolate it from any further processing until deletion is possible.
607
+
608
+ ### Data Minimization
609
+
610
+ We practice data minimization, which means we only collect and process the personal data that is necessary for the purposes for which it is collected. We regularly review our data collection practices to ensure we are not collecting more data than necessary.`,
611
+ included: true,
612
+ variables: ["collectsPersonalData", "dataRetentionPeriod"],
613
+ },
614
+ ],
615
+ };
616
+
617
+ return (
618
+ <div className="container mx-auto p-6">
619
+ <Tabs value={activeTab} onValueChange={setActiveTab}>
620
+ <TabsList>
621
+ <TabsTrigger value="generator">Generator</TabsTrigger>
622
+ <TabsTrigger value="display">Preview</TabsTrigger>
623
+ <TabsTrigger value="audit">Audit & Export</TabsTrigger>
624
+ </TabsList>
625
+
626
+ {/* Generator */}
627
+ <TabsContent value="generator" className="mt-6">
628
+ <Card>
629
+ <CardHeader>
630
+ <CardTitle>Privacy Policy Generator</CardTitle>
631
+ <CardDescription>
632
+ Configure your organization and generate an NDPR‐compliant
633
+ policy.
634
+ </CardDescription>
635
+ </CardHeader>
636
+ <CardContent>
637
+ <PolicyGenerator
638
+ sections={policyTemplate.sections}
639
+ variables={Object.entries(policyTemplate.variables).map(
640
+ ([key, value]) => ({
641
+ id: key,
642
+ name: value.name,
643
+ description: value.description,
644
+ required: value.required,
645
+ defaultValue: value.defaultValue,
646
+ value: value.defaultValue || "",
647
+ inputType: "text", // Default to text input type
648
+ }),
649
+ )}
650
+ onGenerate={handleGeneratePolicy}
651
+ title="NDPR Privacy Policy Generator"
652
+ description="Generate an NDPR-compliant privacy policy for your organization."
653
+ buttonClassName="bg-blue-600 hover:bg-blue-700 text-white"
654
+ />
655
+ </CardContent>
656
+ </Card>
657
+ </TabsContent>
658
+
659
+ {/* Preview */}
660
+ <TabsContent value="display" className="mt-6">
661
+ <Card>
662
+ <CardHeader>
663
+ <CardTitle>Policy Preview</CardTitle>
664
+ <CardDescription>
665
+ Review and edit your generated policy before exporting.
666
+ </CardDescription>
667
+ </CardHeader>
668
+ <CardContent>
669
+ {generatedPolicy && generatedPolicy.length > 0 ? (
670
+ <PolicyPreview
671
+ content={generateFormattedContent()}
672
+ sections={generatedPolicy}
673
+ variables={policyVariables || []}
674
+ onEdit={handlePolicyEdit}
675
+ organizationName={String(
676
+ policyData?.organizationName || "Your Organization",
677
+ )}
678
+ lastUpdated={new Date()}
679
+ showTableOfContents={true}
680
+ showMetadata={true}
681
+ title="Enterprise Privacy Policy"
682
+ description="NDPR-compliant privacy policy ready for implementation"
683
+ buttonClassName="bg-blue-600 hover:bg-blue-700 text-white"
684
+ />
685
+ ) : (
686
+ <div className="p-8 text-center">
687
+ <p className="text-gray-500">
688
+ No policy has been generated yet. Please use the Generator
689
+ tab first.
690
+ </p>
691
+ </div>
692
+ )}
693
+ </CardContent>
694
+ </Card>
695
+ {policyData && policyData.contactEmail ? (
696
+ <div className="mt-6 text-sm text-gray-500">
697
+ Questions? Contact{" "}
698
+ <a href={`mailto:${String(policyData.contactEmail)}`}>
699
+ {String(policyData.contactEmail)}
700
+ </a>
701
+ </div>
702
+ ) : null}
703
+ </TabsContent>
704
+
705
+ {/* Audit & Export */}
706
+ <TabsContent value="audit" className="mt-6">
707
+ <Card>
708
+ <CardHeader>
709
+ <CardTitle>Audit & Export</CardTitle>
710
+ <CardDescription>
711
+ Check compliance and export in multiple formats.
712
+ </CardDescription>
713
+ </CardHeader>
714
+ <CardContent>
715
+ {generatedPolicy && generatedPolicy.length > 0 ? (
716
+ <PolicyExporter
717
+ content={generateFormattedContent()}
718
+ title={`${String(policyData?.organizationName || "Your Org")} Privacy Policy`}
719
+ organizationName={String(
720
+ policyData?.organizationName || "Your Org",
721
+ )}
722
+ lastUpdated={new Date()}
723
+ componentTitle="Export Privacy Policy"
724
+ description="Download in PDF, HTML, or Markdown."
725
+ buttonClassName="bg-blue-600 hover:bg-blue-700 text-white"
726
+ showExportHistory
727
+ includeComplianceNotice
728
+ />
729
+ ) : (
730
+ <div className="p-8 text-center">
731
+ <p className="text-gray-500">
732
+ No policy has been generated yet. Please use the Generator
733
+ tab first.
734
+ </p>
735
+ </div>
736
+ )}
737
+ </CardContent>
738
+ </Card>
739
+ </TabsContent>
740
+ </Tabs>
741
+
742
+ {/* Implementation Notes */}
743
+ <div className="mt-10 p-4 bg-gray-100 rounded-lg">
744
+ <h2 className="text-xl font-semibold mb-2">Implementation Notes</h2>
745
+ <ul className="list-disc pl-5 space-y-1">
746
+ <li>
747
+ <code>PolicyGenerator</code>: Build your policy based on the
748
+ template.
749
+ </li>
750
+ <li>
751
+ <code>PolicyPreview</code>: Live‐edit the generated sections.
752
+ </li>
753
+ <li>
754
+ <code>PolicyExporter</code>: Audit compliance and export final
755
+ documents.
756
+ </li>
757
+ </ul>
758
+ <p className="mt-4">
759
+ For full docs, visit{" "}
760
+ <Link
761
+ href="/docs/components/privacy-policy-generator"
762
+ className="text-blue-600 hover:underline"
763
+ >
764
+ Privacy Policy Generator documentation
765
+ </Link>
766
+ .
767
+ </p>
768
+ </div>
769
+ </div>
770
+ );
771
+ }