@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.
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 -447
  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} +13 -0
  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,1062 @@
1
+ "use client";
2
+
3
+ import React, { useState, useRef } from "react";
4
+ import { PolicySection } from "@/types";
5
+ import { jsPDF } from "jspdf";
6
+ import { escapeHtml } from "@/lib/sanitize";
7
+
8
+ // Import step components
9
+ import OrganizationInfoStep from "./steps/OrganizationInfoStep";
10
+ import DataCollectionStep from "./steps/DataCollectionStep";
11
+ import DataSharingStep from "./steps/DataSharingStep";
12
+ import CustomSectionsStep from "./steps/CustomSectionsStep";
13
+ import PolicyPreviewStep from "./steps/PolicyPreviewStep";
14
+
15
+ // Import shared components
16
+ import StepIndicator from "./shared/StepIndicator";
17
+
18
+ // Import static data
19
+ import {
20
+ defaultDataPurposes,
21
+ defaultSecurityMeasures,
22
+ dataSubjectCategories,
23
+ trackingTechnologies,
24
+ transferSafeguards,
25
+ defaultCookieTypes,
26
+ commonTransferCountries,
27
+ } from "./data";
28
+
29
+ interface PolicyFormData {
30
+ organizationName: string;
31
+ organizationContact: string;
32
+ organizationWebsite: string;
33
+ organizationAddress: string;
34
+ organizationType: string;
35
+ industryCategory: string;
36
+ registrationNumber: string;
37
+ dataCollectionPurposes: string[];
38
+ dataRetentionPeriod: string;
39
+ legalBasisForProcessing: string[];
40
+ dataCategories: string[];
41
+ dataSubjects: string[];
42
+ automatedDecisionMaking: boolean;
43
+ automatedDecisionDetails: string;
44
+ thirdPartySharing: boolean;
45
+ thirdParties: string[];
46
+ thirdPartyCategories: string[];
47
+ thirdPartyPurposes: string[];
48
+ securityMeasures: string[];
49
+ dataBreachProcedures: string;
50
+ regulatoryCompliance: string[];
51
+ cookiesUsed: boolean;
52
+ cookieTypes: string[];
53
+ cookieLifespan: string;
54
+ trackingTechnologies: string[];
55
+ internationalTransfers: boolean;
56
+ transferCountries: string[];
57
+ transferSafeguards: string[];
58
+ processesChildrenData: boolean;
59
+ childrenDataDetails: string;
60
+ processesSpecialCategories: boolean;
61
+ specialCategoriesDetails: string;
62
+ policyEffectiveDate: string;
63
+ policyUpdateProcedure: string;
64
+ policyVersion: string;
65
+ previousPolicyUrl: string;
66
+ dpoContact: string;
67
+ hasDPO: boolean;
68
+ supervisoryAuthorityContact: string;
69
+ customSections: { title: string; template: string }[];
70
+ includeNDPRCompliance: boolean;
71
+ includeLegalReferences: boolean;
72
+ includeExamples: boolean;
73
+ }
74
+
75
+ // Generate safe HTML from form data without using innerHTML
76
+ function generateSafeHTMLFromFormData(formData: PolicyFormData): string {
77
+ const sections: string[] = [];
78
+
79
+ // Title
80
+ sections.push(
81
+ `<h1>${escapeHtml(formData.organizationName)} Privacy Policy</h1>`,
82
+ );
83
+
84
+ // Introduction
85
+ sections.push("<h2>Introduction</h2>");
86
+ sections.push(
87
+ `<p>This Privacy Policy describes how ${escapeHtml(formData.organizationName)} collects, uses, and discloses your personal information.${formData.includeNDPRCompliance ? " This policy is compliant with the Nigeria Data Protection Regulation (NDPR) and the Data Protection Act (DPA)." : ""}</p>`,
88
+ );
89
+
90
+ // About Us
91
+ sections.push("<h2>About Us</h2>");
92
+ sections.push(
93
+ `<p>${escapeHtml(formData.organizationName)}${formData.organizationAddress ? ` is located at ${escapeHtml(formData.organizationAddress)}` : ""}.${formData.organizationWebsite ? ` Our website is ${escapeHtml(formData.organizationWebsite)}.` : ""}</p>`,
94
+ );
95
+
96
+ // Information We Collect
97
+ if (formData.dataCollectionPurposes?.length > 0) {
98
+ sections.push("<h2>Information We Collect</h2>");
99
+ sections.push(
100
+ "<p>We collect and process your personal data for the following purposes:</p>",
101
+ );
102
+ sections.push("<ul>");
103
+ formData.dataCollectionPurposes.forEach((purpose: string) => {
104
+ sections.push(`<li>${escapeHtml(purpose)}</li>`);
105
+ });
106
+ sections.push("</ul>");
107
+ }
108
+
109
+ // Data Retention
110
+ sections.push("<h2>Data Retention</h2>");
111
+ sections.push(
112
+ `<p>We will retain your personal data for ${escapeHtml(formData.dataRetentionPeriod || "a period appropriate to the purpose")}, or for as long as necessary to fulfill the purposes for which it was collected.</p>`,
113
+ );
114
+
115
+ // Third-Party Sharing
116
+ if (formData.thirdPartySharing && formData.thirdParties?.length > 0) {
117
+ sections.push("<h2>Third-Party Sharing</h2>");
118
+ sections.push(
119
+ "<p>We may share your personal information with the following third parties:</p>",
120
+ );
121
+ sections.push("<ul>");
122
+ formData.thirdParties.forEach((party: string) => {
123
+ sections.push(`<li>${escapeHtml(party)}</li>`);
124
+ });
125
+ sections.push("</ul>");
126
+ }
127
+
128
+ // Cookies
129
+ if (formData.cookiesUsed && formData.cookieTypes?.length > 0) {
130
+ sections.push("<h2>Cookies and Tracking Technologies</h2>");
131
+ sections.push(
132
+ "<p>Our website uses cookies and similar tracking technologies. We use the following types of cookies:</p>",
133
+ );
134
+ sections.push("<ul>");
135
+ formData.cookieTypes.forEach((type: string) => {
136
+ sections.push(`<li>${escapeHtml(type)}</li>`);
137
+ });
138
+ sections.push("</ul>");
139
+ }
140
+
141
+ // International Transfers
142
+ if (
143
+ formData.internationalTransfers &&
144
+ formData.transferCountries?.length > 0
145
+ ) {
146
+ sections.push("<h2>International Data Transfers</h2>");
147
+ sections.push(
148
+ "<p>We may transfer your personal data to the following countries outside Nigeria:</p>",
149
+ );
150
+ sections.push("<ul>");
151
+ formData.transferCountries.forEach((country: string) => {
152
+ sections.push(`<li>${escapeHtml(country)}</li>`);
153
+ });
154
+ sections.push("</ul>");
155
+ sections.push(
156
+ "<p>We ensure appropriate safeguards are in place to protect your data when transferred internationally.</p>",
157
+ );
158
+ }
159
+
160
+ // Security Measures
161
+ if (formData.securityMeasures?.length > 0) {
162
+ sections.push("<h2>Security Measures</h2>");
163
+ sections.push(
164
+ "<p>We implement the following security measures to protect your personal data:</p>",
165
+ );
166
+ sections.push("<ul>");
167
+ formData.securityMeasures.forEach((measure: string) => {
168
+ sections.push(`<li>${escapeHtml(measure)}</li>`);
169
+ });
170
+ sections.push("</ul>");
171
+ }
172
+
173
+ // Your Rights
174
+ sections.push("<h2>Your Rights</h2>");
175
+ sections.push(
176
+ `<p>${formData.includeNDPRCompliance ? "Under the NDPR and DPA, you have the following rights:" : "You have the following rights regarding your personal data:"}</p>`,
177
+ );
178
+ sections.push("<ul>");
179
+ sections.push("<li>Right to access your personal data</li>");
180
+ sections.push("<li>Right to rectify inaccurate personal data</li>");
181
+ sections.push('<li>Right to erasure ("right to be forgotten")</li>');
182
+ sections.push("<li>Right to restrict processing</li>");
183
+ sections.push("<li>Right to object to processing</li>");
184
+ sections.push("<li>Right to data portability</li>");
185
+ sections.push("</ul>");
186
+ sections.push(
187
+ `<p>To exercise these rights, please contact us at ${escapeHtml(formData.organizationContact)}.</p>`,
188
+ );
189
+
190
+ // Custom Sections
191
+ if (formData.customSections?.length > 0) {
192
+ formData.customSections.forEach((section) => {
193
+ sections.push(`<h2>${escapeHtml(section.title)}</h2>`);
194
+ sections.push(`<p>${escapeHtml(section.template)}</p>`);
195
+ });
196
+ }
197
+
198
+ // DPO
199
+ if (formData.hasDPO && formData.dpoContact) {
200
+ sections.push("<h2>Data Protection Officer</h2>");
201
+ sections.push(
202
+ `<p>You can contact our Data Protection Officer at ${escapeHtml(formData.dpoContact)}.</p>`,
203
+ );
204
+ }
205
+
206
+ // Contact Us
207
+ sections.push("<h2>Contact Us</h2>");
208
+ sections.push(
209
+ `<p>If you have any questions about this Privacy Policy, please contact us at ${escapeHtml(formData.organizationContact)}.</p>`,
210
+ );
211
+
212
+ // Last Updated
213
+ sections.push(
214
+ `<p>Last Updated: ${escapeHtml(formData.policyEffectiveDate || new Date().toISOString().split("T")[0])}</p>`,
215
+ );
216
+
217
+ return sections.join("\n");
218
+ }
219
+
220
+ interface PolicyGeneratorProps {
221
+ onGenerate: (policy: {
222
+ organizationName: string;
223
+ organizationContact: string;
224
+ sections: PolicySection[];
225
+ }) => void;
226
+ className?: string;
227
+ }
228
+
229
+ export default function PolicyGenerator({
230
+ onGenerate,
231
+ className = "",
232
+ }: PolicyGeneratorProps) {
233
+ const [step, setStep] = useState(1);
234
+ const [formData, setFormData] = useState({
235
+ // Organization Information
236
+ organizationName: "",
237
+ organizationContact: "",
238
+ organizationWebsite: "",
239
+ organizationAddress: "",
240
+ organizationType: "", // New: Type of organization (e.g., e-commerce, healthcare, fintech)
241
+ industryCategory: "", // New: Industry category for contextual examples
242
+ registrationNumber: "", // New: Business registration number
243
+
244
+ // Data Processing Information
245
+ dataCollectionPurposes: [] as string[],
246
+ dataRetentionPeriod: "",
247
+ legalBasisForProcessing: [] as string[], // New: Legal basis under NDPR/DPA
248
+ dataCategories: [] as string[], // New: Categories of personal data collected
249
+ dataSubjects: [] as string[], // New: Categories of data subjects
250
+ automatedDecisionMaking: false, // New: Whether automated decision-making is used
251
+ automatedDecisionDetails: "", // New: Details about automated decision-making
252
+
253
+ // Data Sharing Information
254
+ thirdPartySharing: false,
255
+ thirdParties: [] as string[],
256
+ thirdPartyCategories: [] as string[], // New: Categories of third parties
257
+ thirdPartyPurposes: [] as string[], // New: Purposes of third-party sharing
258
+
259
+ // Security and Compliance
260
+ securityMeasures: [] as string[],
261
+ dataBreachProcedures: "", // New: Data breach notification procedures
262
+ regulatoryCompliance: [] as string[], // New: Additional regulations complied with
263
+
264
+ // Cookies and Tracking
265
+ cookiesUsed: false,
266
+ cookieTypes: [] as string[],
267
+ cookieLifespan: "", // New: Cookie lifespan information
268
+ trackingTechnologies: [] as string[], // New: Other tracking technologies used
269
+
270
+ // International Transfers
271
+ internationalTransfers: false,
272
+ transferCountries: [] as string[],
273
+ transferSafeguards: [] as string[], // New: Safeguards for international transfers
274
+
275
+ // Special Categories
276
+ processesChildrenData: false, // New: Whether children's data is processed
277
+ childrenDataDetails: "", // New: Details about children's data processing
278
+ processesSpecialCategories: false, // New: Whether special category data is processed
279
+ specialCategoriesDetails: "", // New: Details about special category data
280
+
281
+ // Policy Management
282
+ policyEffectiveDate: new Date().toISOString().split("T")[0], // New: Policy effective date
283
+ policyUpdateProcedure: "", // New: How policy updates are communicated
284
+ policyVersion: "1.0", // New: Policy version number
285
+ previousPolicyUrl: "", // New: Link to previous policy version
286
+
287
+ // Contact Information
288
+ dpoContact: "",
289
+ hasDPO: false,
290
+ supervisoryAuthorityContact: "", // New: Contact for supervisory authority
291
+
292
+ // Customization
293
+ customSections: [] as { title: string; template: string }[],
294
+ includeNDPRCompliance: true,
295
+ includeLegalReferences: true, // Whether to include specific legal references
296
+ includeExamples: true, // Whether to include industry-specific examples
297
+ });
298
+
299
+ const [errors, setErrors] = useState<Record<string, string>>({});
300
+ const previewRef = useRef<HTMLDivElement>(null);
301
+
302
+ // Handle form field changes
303
+ const handleChange = (
304
+ e: React.ChangeEvent<
305
+ HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
306
+ >,
307
+ ) => {
308
+ const { name, value, type } = e.target as HTMLInputElement;
309
+
310
+ if (type === "checkbox") {
311
+ setFormData((prev) => ({
312
+ ...prev,
313
+ [name]: (e.target as HTMLInputElement).checked,
314
+ }));
315
+ } else {
316
+ setFormData((prev) => ({
317
+ ...prev,
318
+ [name]: value,
319
+ }));
320
+ }
321
+
322
+ // Clear error when field is edited
323
+ if (errors[name]) {
324
+ setErrors((prev) => {
325
+ const newErrors = { ...prev };
326
+ delete newErrors[name];
327
+ return newErrors;
328
+ });
329
+ }
330
+ };
331
+
332
+ // Handle checkbox group selections
333
+ const handleMultiSelect = (
334
+ category:
335
+ | "dataCollectionPurposes"
336
+ | "securityMeasures"
337
+ | "cookieTypes"
338
+ | "transferCountries"
339
+ | "dataSubjects"
340
+ | "dataCategories"
341
+ | "legalBasisForProcessing"
342
+ | "thirdPartyCategories"
343
+ | "thirdPartyPurposes"
344
+ | "trackingTechnologies"
345
+ | "transferSafeguards"
346
+ | "regulatoryCompliance",
347
+ item: string,
348
+ ) => {
349
+ setFormData((prev) => {
350
+ const currentItems = [...prev[category]];
351
+ if (currentItems.includes(item)) {
352
+ return {
353
+ ...prev,
354
+ [category]: currentItems.filter((i) => i !== item),
355
+ };
356
+ } else {
357
+ return {
358
+ ...prev,
359
+ [category]: [...currentItems, item],
360
+ };
361
+ }
362
+ });
363
+ };
364
+
365
+ // Add a third party to the list
366
+ const handleAddThirdParty = () => {
367
+ const thirdPartyInput = document.getElementById(
368
+ "thirdPartyInput",
369
+ ) as HTMLInputElement;
370
+ if (thirdPartyInput && thirdPartyInput.value.trim()) {
371
+ setFormData((prev) => ({
372
+ ...prev,
373
+ thirdParties: [...prev.thirdParties, thirdPartyInput.value.trim()],
374
+ }));
375
+ thirdPartyInput.value = "";
376
+ }
377
+ };
378
+
379
+ // Remove a third party from the list
380
+ const handleRemoveThirdParty = (index: number) => {
381
+ setFormData((prev) => ({
382
+ ...prev,
383
+ thirdParties: prev.thirdParties.filter((_, i) => i !== index),
384
+ }));
385
+ };
386
+
387
+ // Add a custom section to the policy
388
+ const handleAddCustomSection = (title: string, template: string) => {
389
+ if (title.trim() && template.trim()) {
390
+ setFormData((prev) => ({
391
+ ...prev,
392
+ customSections: [
393
+ ...prev.customSections,
394
+ { title: title.trim(), template: template.trim() },
395
+ ],
396
+ }));
397
+ }
398
+ };
399
+
400
+ // Remove a custom section from the policy
401
+ const handleRemoveCustomSection = (index: number) => {
402
+ setFormData((prev) => ({
403
+ ...prev,
404
+ customSections: prev.customSections.filter((_, i) => i !== index),
405
+ }));
406
+ };
407
+
408
+ // Validate each step of the form with enhanced validation
409
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
410
+ const validateStep = (_step: number) => {
411
+ // Clear any existing errors
412
+ setErrors({});
413
+
414
+ // Always return true to allow navigation through all steps
415
+ return true;
416
+ };
417
+
418
+ // Move to the next step if validation passes
419
+ const handleNext = () => {
420
+ if (validateStep(step)) {
421
+ setStep((prev) => prev + 1);
422
+ }
423
+ };
424
+
425
+ // Move to the previous step
426
+ const handleBack = () => {
427
+ setStep((prev) => prev - 1);
428
+ };
429
+
430
+ // These handler functions have been moved to the final policy page
431
+ // but are kept here for reference and to avoid TypeScript errors
432
+ // The underscore prefix indicates they're unused
433
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
434
+ const _handleCopyPolicy = () => {
435
+ if (previewRef.current) {
436
+ const policyText = previewRef.current.innerText;
437
+ navigator.clipboard
438
+ .writeText(policyText)
439
+ .then(() => {
440
+ alert("Privacy policy copied to clipboard!");
441
+ })
442
+ .catch((err) => {
443
+ console.error("Failed to copy text: ", err);
444
+ alert("Failed to copy text. Please try again.");
445
+ });
446
+ }
447
+ };
448
+
449
+ // Share policy via email
450
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
451
+ const _handleSharePolicy = () => {
452
+ if (previewRef.current) {
453
+ const subject = `${formData.organizationName} - Privacy Policy`;
454
+ const body = previewRef.current.innerText;
455
+ const mailtoLink = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
456
+ window.open(mailtoLink);
457
+ }
458
+ };
459
+
460
+ // Download policy in different formats
461
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
462
+ const _handleDownloadPolicy = async (
463
+ format: "txt" | "html" | "md" | "pdf" | "rtf",
464
+ ) => {
465
+ if (previewRef.current) {
466
+ let content = "";
467
+ let mimeType = "";
468
+ let fileExtension = "";
469
+ let blob: Blob | null = null;
470
+
471
+ const policyText = previewRef.current.innerText;
472
+ // Instead of using innerHTML directly, we'll generate safe HTML from the form data
473
+ const policyHTML = generateSafeHTMLFromFormData(formData);
474
+ const policyTitle = `${formData.organizationName} Privacy Policy`;
475
+ const fileName = `${formData.organizationName.replace(/\s+/g, "-").toLowerCase()}-privacy-policy`;
476
+
477
+ // Convert policy to markdown
478
+ const convertToMarkdown = (html: string): string => {
479
+ // Simple HTML to Markdown conversion
480
+ let md = html
481
+ .replace(/<h1>(.*?)<\/h1>/g, "# $1\n\n")
482
+ .replace(/<h2>(.*?)<\/h2>/g, "## $1\n\n")
483
+ .replace(/<h3>(.*?)<\/h3>/g, "### $1\n\n")
484
+ .replace(/<p>(.*?)<\/p>/g, "$1\n\n")
485
+ .replace(/<strong>(.*?)<\/strong>/g, "**$1**")
486
+ .replace(/<em>(.*?)<\/em>/g, "*$1*")
487
+ .replace(/<ul>(.*?)<\/ul>/g, "$1\n")
488
+ .replace(/<li>(.*?)<\/li>/g, "- $1\n")
489
+ .replace(/<br\s*\/?>/g, "\n")
490
+ .replace(/&nbsp;/g, " ");
491
+
492
+ // Remove any remaining HTML tags
493
+ md = md.replace(/<[^>]*>/g, "");
494
+
495
+ return md;
496
+ };
497
+
498
+ try {
499
+ switch (format) {
500
+ case "pdf":
501
+ // Create PDF using jsPDF
502
+ const pdf = new jsPDF({
503
+ orientation: "portrait",
504
+ unit: "mm",
505
+ format: "a4",
506
+ });
507
+
508
+ // Add title
509
+ pdf.setFontSize(16);
510
+ pdf.text(policyTitle, 20, 20);
511
+ pdf.setFontSize(12);
512
+
513
+ // Split text into lines to fit on PDF page
514
+ const textLines = pdf.splitTextToSize(policyText, 170);
515
+ pdf.text(textLines, 20, 30);
516
+
517
+ // Get PDF as blob
518
+ blob = pdf.output("blob");
519
+ mimeType = "application/pdf";
520
+ fileExtension = "pdf";
521
+ break;
522
+
523
+ case "rtf":
524
+ // For DOCX, we'll create a simple text file with .docx extension
525
+ // This is a workaround since browser-based DOCX generation is limited
526
+ content = policyText;
527
+ mimeType =
528
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
529
+ fileExtension = "rtf";
530
+ blob = new Blob([content], { type: mimeType });
531
+ break;
532
+
533
+ case "html":
534
+ content = policyHTML;
535
+ mimeType = "text/html";
536
+ fileExtension = "html";
537
+ blob = new Blob([content], { type: mimeType });
538
+ break;
539
+
540
+ case "md":
541
+ content = convertToMarkdown(policyHTML);
542
+ mimeType = "text/markdown";
543
+ fileExtension = "md";
544
+ blob = new Blob([content], { type: mimeType });
545
+ break;
546
+
547
+ case "txt":
548
+ default:
549
+ content = policyText;
550
+ mimeType = "text/plain";
551
+ fileExtension = "txt";
552
+ blob = new Blob([content], { type: mimeType });
553
+ break;
554
+ }
555
+
556
+ if (blob) {
557
+ const url = URL.createObjectURL(blob);
558
+ const a = document.createElement("a");
559
+ a.href = url;
560
+ a.download = `${fileName}.${fileExtension}`;
561
+ document.body.appendChild(a);
562
+ a.click();
563
+ document.body.removeChild(a);
564
+ URL.revokeObjectURL(url);
565
+ }
566
+ } catch (error) {
567
+ console.error("Error generating document:", error);
568
+ alert("There was an error generating your document. Please try again.");
569
+ }
570
+ }
571
+ };
572
+
573
+ // Generate the final privacy policy and pass it to the parent component
574
+ const handleGenerate = () => {
575
+ if (!validateStep(step)) return;
576
+
577
+ // Create policy sections
578
+ const sections: PolicySection[] = [
579
+ {
580
+ id: "introduction",
581
+ title: "Introduction",
582
+ template: `This Privacy Policy ("Policy") describes how ${formData.organizationName} ("we", "us", or "our") collects, uses, and discloses your personal information when you visit our website${formData.organizationWebsite ? ` at ${formData.organizationWebsite}` : ""}, use our services, or otherwise interact with us. This Policy applies to all personal data processed by us, regardless of the media on which it is stored. ${formData.includeNDPRCompliance ? `This Policy is designed to comply with the Nigeria Data Protection Regulation (NDPR) ${formData.includeLegalReferences ? "of 2019 " : ""}and the Data Protection Act (DPA) ${formData.includeLegalReferences ? "of 2023 " : ""}and reflects our commitment to the principles of data protection as outlined in ${formData.includeLegalReferences ? "Section 2.1(1) of the NDPR and Section 2 of the DPA" : "the applicable regulations"}.` : ""} Please read this Policy carefully to understand our practices regarding your personal data and how we will treat it.`,
583
+ required: true,
584
+ included: true,
585
+ order: 1,
586
+ },
587
+ {
588
+ id: "about-us",
589
+ title: "About Us",
590
+ template: `${formData.organizationName} ${formData.organizationType ? `is a ${formData.organizationType} operating in the ${formData.industryCategory} sector` : ""}${formData.registrationNumber ? `, registered with registration number ${formData.registrationNumber}` : ""}${formData.organizationAddress ? `, and located at ${formData.organizationAddress}` : ""}. ${formData.organizationWebsite ? `Our official website is ${formData.organizationWebsite}.` : ""} ${formData.includeNDPRCompliance ? `As a data controller under the NDPR and DPA, we are responsible for deciding how we hold and use personal information about you.` : ""}`,
591
+ required: true,
592
+ included: true,
593
+ order: 2,
594
+ },
595
+ {
596
+ id: "definitions",
597
+ title: "Definitions",
598
+ template: `${formData.includeNDPRCompliance ? `For the purposes of this Policy and in accordance with the NDPR and DPA:\n\n- "Personal Data" means 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.\n- "Processing" means any operation or set of operations performed on personal data or on sets of personal data.\n- "Data Controller" means a person who either alone, jointly with other persons or in common with other persons or as a statutory body determines the purposes for and the manner in which personal data is processed or is to be processed.\n- "Data Subject" means any person, who can be identified, directly or indirectly, by reference to an identification number or to one or more factors specific to his physical, physiological, mental, economic, cultural or social identity.\n- "Consent" means any freely given, specific, informed and unambiguous indication of the data subject's wishes by which he or she, through a statement or a clear affirmative action, signifies agreement to the processing of personal data relating to him or her.` : `For the purposes of this Policy:\n\n- "Personal Data" means any information relating to an identified or identifiable individual.\n- "Processing" means any operation performed on personal data.\n- "Data Subject" means the individual to whom the personal data relates.`}`,
599
+ required: true,
600
+ included: true,
601
+ order: 3,
602
+ },
603
+ ];
604
+
605
+ let order = 4;
606
+
607
+ // Add data categories section
608
+ if (formData.dataCategories.length > 0) {
609
+ sections.push({
610
+ id: "data-categories",
611
+ title: "Categories of Personal Data We Collect",
612
+ template: `We collect and process the following categories of personal data:\n\n${formData.dataCategories.map((category) => `- ${category}`).join("\n")}${formData.includeExamples ? `\n\n${getIndustrySpecificDataExample(formData.industryCategory)}` : ""}`,
613
+ required: true,
614
+ included: true,
615
+ order: order++,
616
+ });
617
+ }
618
+
619
+ // Add data subjects section
620
+ if (formData.dataSubjects.length > 0) {
621
+ sections.push({
622
+ id: "data-subjects",
623
+ title: "Categories of Data Subjects",
624
+ template: `This Policy applies to personal data we collect from the following categories of individuals:\n\n${formData.dataSubjects.map((subject) => `- ${subject}`).join("\n")}`,
625
+ required: true,
626
+ included: true,
627
+ order: order++,
628
+ });
629
+ }
630
+
631
+ // Add data collection purposes section
632
+ sections.push({
633
+ id: "data-collection-purposes",
634
+ title: "Purposes of Data Collection and Processing",
635
+ template: `We collect and process your personal data for the following specific purposes:\n\n${formData.dataCollectionPurposes.map((purpose) => `- ${purpose}`).join("\n")}${formData.includeExamples ? `\n\n${getIndustrySpecificPurposeExample(formData.industryCategory)}` : ""}`,
636
+ required: true,
637
+ included: true,
638
+ order: order++,
639
+ });
640
+
641
+ if (formData.legalBasisForProcessing.length > 0) {
642
+ sections.push({
643
+ id: "legal-basis",
644
+ title: "Legal Basis for Processing",
645
+ template: `${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.2 of the NDPR and Section 27 of the DPA" : "the NDPR and DPA"}, we process your personal data on the following legal grounds:` : "We process your personal data on the following legal grounds:"}\n\n${formData.legalBasisForProcessing.map((basis) => `- ${basis}`).join("\n")}`,
646
+ required: true,
647
+ included: true,
648
+ order: order++,
649
+ });
650
+ }
651
+
652
+ if (formData.automatedDecisionMaking) {
653
+ sections.push({
654
+ id: "automated-decision-making",
655
+ title: "Automated Decision-Making and Profiling",
656
+ template: `We use automated decision-making processes, including profiling, in the following circumstances:\n\n${formData.automatedDecisionDetails}\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.3(1)(c) of the NDPR and Section 41 of the DPA" : "the NDPR and DPA"}, 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. You can exercise this right by contacting us at ${formData.organizationContact}.` : "You have the right not to be subject to a decision based solely on automated processing. You can exercise this right by contacting us."}`,
657
+ required: false,
658
+ included: true,
659
+ order: order++,
660
+ });
661
+ }
662
+
663
+ // Add data retention section
664
+ sections.push({
665
+ id: "data-retention",
666
+ title: "Data Retention",
667
+ template: `We will retain your personal data for ${formData.dataRetentionPeriod}, or for as long as necessary to fulfill the purposes for which it was collected, including for the purposes of satisfying any legal, accounting, or reporting requirements. ${formData.includeNDPRCompliance ? `This is in accordance with ${formData.includeLegalReferences ? "Section 2.1(1)(e) of the NDPR and Section 33 of the DPA" : "the data retention principles of the NDPR and DPA"}.` : ""}\n\nTo 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.`,
668
+ required: true,
669
+ included: true,
670
+ order: order++,
671
+ });
672
+
673
+ // Add third-party sharing section if applicable
674
+ if (formData.thirdPartySharing) {
675
+ let thirdPartyContent =
676
+ "We may share your personal information with the following categories of recipients:";
677
+
678
+ if (formData.thirdPartyCategories.length > 0) {
679
+ thirdPartyContent += `\n\nCategories of third parties:\n${formData.thirdPartyCategories.map((category) => `- ${category}`).join("\n")}`;
680
+ }
681
+
682
+ if (formData.thirdParties.length > 0) {
683
+ thirdPartyContent += `\n\nSpecific third parties:\n${formData.thirdParties.map((party) => `- ${party}`).join("\n")}`;
684
+ }
685
+
686
+ if (formData.thirdPartyPurposes.length > 0) {
687
+ thirdPartyContent += `\n\nWe share your personal data with these third parties for the following purposes:\n${formData.thirdPartyPurposes.map((purpose) => `- ${purpose}`).join("\n")}`;
688
+ }
689
+
690
+ thirdPartyContent += `\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.6 of the NDPR and Section 37 of the DPA" : "the NDPR and DPA"}, 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.` : "We require all third parties to respect the security of your personal data and to treat it in accordance with applicable law."}`;
691
+
692
+ sections.push({
693
+ id: "third-party-sharing",
694
+ title: "Third-Party Sharing",
695
+ template: thirdPartyContent,
696
+ required: false,
697
+ included: true,
698
+ order: order++,
699
+ });
700
+ }
701
+
702
+ // Add cookies section if applicable
703
+ if (formData.cookiesUsed) {
704
+ let cookieContent = `Our website uses cookies and similar tracking technologies to distinguish you from other users of our website. This helps us to provide you with a good experience when you browse our website and also allows us to improve our site.`;
705
+
706
+ if (formData.cookieTypes.length > 0) {
707
+ cookieContent += `\n\nWe use the following types of cookies:\n${formData.cookieTypes.map((type) => `- ${type}`).join("\n")}`;
708
+ }
709
+
710
+ if (formData.cookieLifespan) {
711
+ cookieContent += `\n\nCookie Lifespan: ${formData.cookieLifespan}`;
712
+ }
713
+
714
+ if (formData.trackingTechnologies.length > 0) {
715
+ cookieContent += `\n\nIn addition to cookies, we also use the following tracking technologies:\n${formData.trackingTechnologies.map((tech) => `- ${tech}`).join("\n")}`;
716
+ }
717
+
718
+ cookieContent += `\n\nYou can set your browser to refuse all or some browser cookies, or to alert you when websites set or access cookies. If you disable or refuse cookies, please note that some parts of this website may become inaccessible or not function properly.`;
719
+
720
+ sections.push({
721
+ id: "cookies",
722
+ title: "Cookies and Tracking Technologies",
723
+ template: cookieContent,
724
+ required: false,
725
+ included: true,
726
+ order: order++,
727
+ });
728
+ }
729
+
730
+ // Add international transfers section if applicable
731
+ if (formData.internationalTransfers) {
732
+ let transferContent = `We may transfer your personal data to countries outside Nigeria. These countries may include: ${formData.transferCountries.join(", ")}.`;
733
+
734
+ if (formData.transferSafeguards.length > 0) {
735
+ transferContent += `\n\nWhenever we transfer your personal data out of Nigeria, we ensure a similar degree of protection is afforded to it by implementing the following safeguards:\n${formData.transferSafeguards.map((safeguard) => `- ${safeguard}`).join("\n")}`;
736
+ }
737
+
738
+ transferContent += `\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Sections 2.11 and 2.12 of the NDPR and Section 44 of the DPA" : "the NDPR and DPA"}, we ensure that any international transfer of personal data is done in accordance with the provisions of the regulations and that adequate protection is guaranteed for the rights of data subjects.` : "We ensure that any international transfer of personal data is done with adequate protection for the rights of data subjects."}`;
739
+
740
+ sections.push({
741
+ id: "international-transfers",
742
+ title: "International Data Transfers",
743
+ template: transferContent,
744
+ required: false,
745
+ included: true,
746
+ order: order++,
747
+ });
748
+ }
749
+
750
+ // Add children's data section if applicable
751
+ if (formData.processesChildrenData) {
752
+ sections.push({
753
+ id: "childrens-data",
754
+ title: "Children's Privacy",
755
+ template: `Our services may be used by individuals under the age of 18. ${formData.childrenDataDetails}\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.2(d) of the NDPR and Section 39 of the DPA" : "the NDPR and DPA"}, we implement specific measures to protect the privacy of children, including obtaining parental consent where required by law.` : "We implement specific measures to protect the privacy of children, including obtaining parental consent where required by law."}`,
756
+ required: false,
757
+ included: true,
758
+ order: order++,
759
+ });
760
+ }
761
+
762
+ // Add special categories section if applicable
763
+ if (formData.processesSpecialCategories) {
764
+ sections.push({
765
+ id: "special-categories",
766
+ title: "Special Categories of Personal Data",
767
+ template: `We may process special categories of personal data, which includes information about your race, ethnic origin, political opinions, religious or philosophical beliefs, trade union membership, genetic data, biometric data, health data, sex life, or sexual orientation.\n\n${formData.specialCategoriesDetails}\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.2 of the NDPR and Section 28 of the DPA" : "the NDPR and DPA"}, we only process special categories of personal data when one of the specific legal bases for such processing is met, such as explicit consent or when processing is necessary for specific purposes outlined in the regulations.` : "We only process special categories of personal data when one of the specific legal bases for such processing is met, such as explicit consent."}`,
768
+ required: false,
769
+ included: true,
770
+ order: order++,
771
+ });
772
+ }
773
+
774
+ // Add security measures section
775
+ if (formData.securityMeasures.length > 0) {
776
+ let securityContent = `We have implemented appropriate security measures to prevent your personal data from being accidentally lost, used, or accessed in an unauthorized way, altered, or disclosed. These measures include:\n\n${formData.securityMeasures.map((measure) => `- ${measure}`).join("\n")}\n\n${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 2.1(1)(d) of the NDPR and Section 31 of the DPA" : "the NDPR and DPA"}, we implement appropriate technical and organizational measures to ensure a level of security appropriate to the risk.` : "We regularly review and update our security measures to ensure the ongoing confidentiality, integrity, and availability of your personal data."}`;
777
+
778
+ if (formData.dataBreachProcedures) {
779
+ securityContent += `\n\nData Breach Procedures: ${formData.dataBreachProcedures}`;
780
+ }
781
+
782
+ sections.push({
783
+ id: "security-measures",
784
+ title: "Data Security",
785
+ template: securityContent,
786
+ required: true,
787
+ included: true,
788
+ order: order++,
789
+ });
790
+ }
791
+
792
+ // Add data subject rights section
793
+ const rightsContent = `${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 3.1 of the NDPR and Section 36 of the DPA" : "the NDPR and DPA"}, you have the following rights in relation to your personal data:` : "You have the following rights in relation to your personal data:"}\n\n- **Right to Access**: You have the right to request a copy of the personal data we hold about you.\n- **Right to Rectification**: You have the right to request correction of any inaccurate personal data we hold about you.\n- **Right to Erasure (Right to be Forgotten)**: You have the right to request erasure of your personal data in certain circumstances.\n- **Right to Restriction of Processing**: You have the right to request restriction of processing of your personal data in certain circumstances.\n- **Right to Data Portability**: You have the right to request the transfer of your personal data to you or to a third party in a structured, commonly used, machine-readable format.\n- **Right to Object**: You have the right to object to processing of your personal data in certain circumstances.\n- **Right to Withdraw Consent**: Where we rely on your consent to process your personal data, you have the right to withdraw your consent at any time.\n\nTo exercise any of these rights, please contact us at ${formData.organizationContact}.${formData.includeNDPRCompliance ? ` We will respond to your request within ${formData.includeLegalReferences ? "30 days as required by Section 3.1(7) of the NDPR" : "the timeframe specified by the regulations"}.` : ""}`;
794
+
795
+ sections.push({
796
+ id: "data-subject-rights",
797
+ title: "Your Rights",
798
+ template: rightsContent,
799
+ required: true,
800
+ included: true,
801
+ order: order++,
802
+ });
803
+
804
+ // Add policy updates section
805
+ sections.push({
806
+ id: "policy-updates",
807
+ title: "Changes to This Privacy Policy",
808
+ template: `We may update this Privacy Policy from time to time. The current version of the Privacy Policy is effective as of ${formData.policyEffectiveDate} (Version ${formData.policyVersion}).${formData.previousPolicyUrl ? ` Previous versions of this Policy can be found at ${formData.previousPolicyUrl}.` : ""}\n\n${formData.policyUpdateProcedure ? `When we make changes to this Privacy Policy: ${formData.policyUpdateProcedure}` : "We will notify you of any material changes to this Privacy Policy by posting the updated Policy on our website and, where appropriate, by sending you a notification."}`,
809
+ required: true,
810
+ included: true,
811
+ order: order++,
812
+ });
813
+
814
+ // Add regulatory compliance section if applicable
815
+ if (formData.regulatoryCompliance.length > 0) {
816
+ sections.push({
817
+ id: "regulatory-compliance",
818
+ title: "Regulatory Compliance",
819
+ template: `In addition to the ${formData.includeNDPRCompliance ? "NDPR and DPA" : "applicable data protection laws"}, we comply with the following regulations and standards:\n\n${formData.regulatoryCompliance.map((reg) => `- ${reg}`).join("\n")}`,
820
+ required: false,
821
+ included: true,
822
+ order: order++,
823
+ });
824
+ }
825
+
826
+ // Add custom sections
827
+ formData.customSections.forEach((section, index) => {
828
+ sections.push({
829
+ id: `custom-${index}`,
830
+ title: section.title,
831
+ template: section.template,
832
+ required: false,
833
+ included: true,
834
+ order: order++,
835
+ });
836
+ });
837
+
838
+ // Add DPO section if applicable
839
+ if (formData.hasDPO && formData.dpoContact) {
840
+ sections.push({
841
+ id: "dpo",
842
+ title: "Data Protection Officer",
843
+ template: `We have appointed a Data Protection Officer (DPO) who is responsible for overseeing questions in relation to this Privacy Policy. You can contact our DPO at ${formData.dpoContact}.${formData.includeNDPRCompliance ? ` This appointment is in accordance with ${formData.includeLegalReferences ? "Section 2.5 of the NDPR and Section 30 of the DPA" : "the requirements of the NDPR and DPA"}.` : ""}`,
844
+ required: false,
845
+ included: true,
846
+ order: order++,
847
+ });
848
+ }
849
+
850
+ // Add supervisory authority section if applicable
851
+ if (formData.supervisoryAuthorityContact) {
852
+ sections.push({
853
+ id: "supervisory-authority",
854
+ title: "Supervisory Authority",
855
+ template: `${formData.includeNDPRCompliance ? `In accordance with ${formData.includeLegalReferences ? "Section 3.1(8) of the NDPR and Section 36(5) of the DPA" : "the NDPR and DPA"}, you have the right to lodge a complaint with the Nigeria Data Protection Commission (NDPC) if you are not satisfied with our response to your concerns. You can contact the NDPC at ${formData.supervisoryAuthorityContact}.` : `You have the right to lodge a complaint with the relevant data protection authority if you are not satisfied with our response to your concerns. You can contact them at ${formData.supervisoryAuthorityContact}.`}`,
856
+ required: false,
857
+ included: true,
858
+ order: order++,
859
+ });
860
+ }
861
+
862
+ // Add contact section
863
+ sections.push({
864
+ id: "contact",
865
+ title: "Contact Us",
866
+ template: `If you have any questions about this Privacy Policy or our privacy practices, please contact us at:\n\n${formData.organizationName}\n${formData.organizationAddress ? `${formData.organizationAddress}\n` : ""}Email: ${formData.organizationContact}\n${formData.organizationWebsite ? `Website: ${formData.organizationWebsite}` : ""}`,
867
+ required: true,
868
+ included: true,
869
+ order: order++,
870
+ });
871
+
872
+ // Pass the generated policy to the parent component
873
+ onGenerate({
874
+ organizationName: formData.organizationName,
875
+ organizationContact: formData.organizationContact,
876
+ sections,
877
+ });
878
+ };
879
+
880
+ // Helper function to get industry-specific examples for data collection
881
+ const getIndustrySpecificDataExample = (industry: string): string => {
882
+ switch (industry) {
883
+ case "E-commerce and retail":
884
+ return "For example, we collect your name, shipping address, and payment information when you place an order on our website.";
885
+ case "Financial services and fintech":
886
+ return "For example, we collect your financial information, transaction history, and credit information to provide our financial services.";
887
+ case "Healthcare and medical":
888
+ return "For example, we collect your medical history, treatment information, and health insurance details to provide healthcare services.";
889
+ case "Education and e-learning":
890
+ return "For example, we collect your educational background, course progress, and assessment results to provide educational services.";
891
+ default:
892
+ return "For example, we collect information necessary to provide our services and ensure a personalized experience.";
893
+ }
894
+ };
895
+
896
+ // Helper function to get industry-specific examples for purposes
897
+ const getIndustrySpecificPurposeExample = (industry: string): string => {
898
+ switch (industry) {
899
+ case "E-commerce and retail":
900
+ return "For example, we use your shipping address to deliver products you order and your payment information to process transactions.";
901
+ case "Financial services and fintech":
902
+ return "For example, we use your financial information to process transactions and your credit information for credit assessments.";
903
+ case "Healthcare and medical":
904
+ return "For example, we use your medical history to provide appropriate healthcare services and treatment recommendations.";
905
+ case "Education and e-learning":
906
+ return "For example, we use your course progress data to personalize your learning experience and provide appropriate educational content.";
907
+ default:
908
+ return "For example, we use your information to provide our services, improve your experience, and ensure compliance with applicable laws.";
909
+ }
910
+ };
911
+
912
+ // Step labels for the progress indicator
913
+ const stepLabels = [
914
+ "Organization Info",
915
+ "Data Collection",
916
+ "Data Sharing",
917
+ "Finalize",
918
+ ];
919
+
920
+ return (
921
+ <div
922
+ className={`bg-white dark:bg-gray-900 shadow-lg rounded-xl p-8 max-w-4xl mx-auto ${className}`}
923
+ >
924
+ <h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-8 text-center">
925
+ Privacy Policy Generator
926
+ </h2>
927
+
928
+ <StepIndicator
929
+ currentStep={step}
930
+ totalSteps={4}
931
+ stepLabels={stepLabels}
932
+ />
933
+
934
+ {/* Step 1: Organization Information */}
935
+ {step === 1 && (
936
+ <OrganizationInfoStep
937
+ formData={formData}
938
+ errors={errors}
939
+ onChange={handleChange}
940
+ />
941
+ )}
942
+
943
+ {/* Step 2: Data Collection */}
944
+ {step === 2 && (
945
+ <DataCollectionStep
946
+ formData={formData}
947
+ errors={errors}
948
+ onChange={handleChange}
949
+ onToggleItem={handleMultiSelect}
950
+ defaultDataPurposes={defaultDataPurposes}
951
+ defaultSecurityMeasures={defaultSecurityMeasures}
952
+ dataSubjectCategories={dataSubjectCategories}
953
+ />
954
+ )}
955
+
956
+ {/* Step 3: Data Sharing */}
957
+ {step === 3 && (
958
+ <DataSharingStep
959
+ formData={formData}
960
+ errors={errors}
961
+ onChange={handleChange}
962
+ onToggleItem={handleMultiSelect}
963
+ onAddThirdParty={handleAddThirdParty}
964
+ onRemoveThirdParty={handleRemoveThirdParty}
965
+ defaultCookieTypes={defaultCookieTypes}
966
+ commonTransferCountries={commonTransferCountries}
967
+ trackingTechnologies={trackingTechnologies}
968
+ transferSafeguards={transferSafeguards}
969
+ />
970
+ )}
971
+
972
+ {/* Step 4: Custom Sections and Preview */}
973
+ {step === 4 && (
974
+ <div className="space-y-8">
975
+ <CustomSectionsStep
976
+ formData={formData}
977
+ onAddCustomSection={handleAddCustomSection}
978
+ onRemoveCustomSection={handleRemoveCustomSection}
979
+ />
980
+
981
+ <PolicyPreviewStep formData={formData} previewRef={previewRef} />
982
+ </div>
983
+ )}
984
+
985
+ <div className="mt-10 flex justify-between items-center">
986
+ {step > 1 ? (
987
+ <button
988
+ type="button"
989
+ onClick={handleBack}
990
+ className="inline-flex items-center px-5 py-2.5 border border-gray-300 dark:border-gray-600 shadow-sm text-sm font-medium rounded-lg text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200 ease-in-out"
991
+ aria-label="Go back to previous step"
992
+ >
993
+ <svg
994
+ xmlns="http://www.w3.org/2000/svg"
995
+ className="h-5 w-5 mr-2"
996
+ viewBox="0 0 20 20"
997
+ fill="currentColor"
998
+ aria-hidden="true"
999
+ >
1000
+ <path
1001
+ fillRule="evenodd"
1002
+ d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
1003
+ clipRule="evenodd"
1004
+ />
1005
+ </svg>
1006
+ Back
1007
+ </button>
1008
+ ) : (
1009
+ <div>
1010
+ {/* Empty div to maintain layout when back button is not shown */}
1011
+ </div>
1012
+ )}
1013
+
1014
+ {step < 4 ? (
1015
+ <button
1016
+ type="button"
1017
+ onClick={handleNext}
1018
+ className="inline-flex items-center px-5 py-2.5 border border-transparent shadow-sm text-sm font-medium rounded-lg text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200 ease-in-out"
1019
+ aria-label="Proceed to next step"
1020
+ >
1021
+ Next
1022
+ <svg
1023
+ xmlns="http://www.w3.org/2000/svg"
1024
+ className="h-5 w-5 ml-2"
1025
+ viewBox="0 0 20 20"
1026
+ fill="currentColor"
1027
+ aria-hidden="true"
1028
+ >
1029
+ <path
1030
+ fillRule="evenodd"
1031
+ d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
1032
+ clipRule="evenodd"
1033
+ />
1034
+ </svg>
1035
+ </button>
1036
+ ) : (
1037
+ <button
1038
+ type="button"
1039
+ onClick={handleGenerate}
1040
+ className="inline-flex items-center px-5 py-2.5 border border-transparent shadow-sm text-sm font-medium rounded-lg text-white bg-gradient-to-r from-green-500 to-emerald-600 hover:from-green-600 hover:to-emerald-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition-all duration-200 ease-in-out"
1041
+ aria-label="Generate privacy policy"
1042
+ >
1043
+ <svg
1044
+ xmlns="http://www.w3.org/2000/svg"
1045
+ className="h-5 w-5 mr-2"
1046
+ viewBox="0 0 20 20"
1047
+ fill="currentColor"
1048
+ aria-hidden="true"
1049
+ >
1050
+ <path
1051
+ fillRule="evenodd"
1052
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
1053
+ clipRule="evenodd"
1054
+ />
1055
+ </svg>
1056
+ Generate Policy
1057
+ </button>
1058
+ )}
1059
+ </div>
1060
+ </div>
1061
+ );
1062
+ }