@tantainnovative/ndpr-toolkit 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +20 -0
- package/.eslintrc.json +10 -0
- package/.github/workflows/ci.yml +36 -0
- package/.github/workflows/nextjs.yml +104 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc.js +4 -0
- package/.nvmrc +1 -0
- package/.versionrc +17 -0
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +90 -0
- package/CNAME +1 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +84 -447
- package/RELEASE-NOTES-v1.0.0.md +140 -0
- package/RELEASE-NOTES-v1.0.1.md +69 -0
- package/SECURITY.md +21 -0
- package/commitlint.config.js +36 -0
- package/components.json +21 -0
- package/eslint.config.mjs +16 -0
- package/jest.config.js +31 -0
- package/jest.setup.js +15 -0
- package/next.config.js +15 -0
- package/next.config.ts +62 -0
- package/package.json +70 -52
- package/packages/ndpr-toolkit/README.md +467 -0
- package/packages/ndpr-toolkit/jest.config.js +23 -0
- package/packages/ndpr-toolkit/package-lock.json +8197 -0
- package/packages/ndpr-toolkit/package.json +71 -0
- package/packages/ndpr-toolkit/rollup.config.js +34 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
- package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
- package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
- package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
- package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
- package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
- package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
- package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
- package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +13 -0
- package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
- package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
- package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
- package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
- package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
- package/packages/ndpr-toolkit/src/types/index.ts +42 -0
- package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
- package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
- package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
- package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
- package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
- package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
- package/packages/ndpr-toolkit/tsconfig.json +23 -0
- package/postcss.config.mjs +5 -0
- package/public/NDPR TOOLKIT.svg +1 -0
- package/public/favicon/android-chrome-192x192.png +0 -0
- package/public/favicon/android-chrome-512x512.png +0 -0
- package/public/favicon/apple-touch-icon.png +0 -0
- package/public/favicon/favicon-16x16.png +0 -0
- package/public/favicon/favicon-32x32.png +0 -0
- package/public/favicon/site.webmanifest +1 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/ndpr-toolkit-logo.svg +108 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/__tests__/example.test.ts +13 -0
- package/src/__tests__/requestService.test.ts +57 -0
- package/src/app/accessibility.css +70 -0
- package/src/app/docs/components/DocLayout.tsx +267 -0
- package/src/app/docs/components/breach-notification/page.tsx +797 -0
- package/src/app/docs/components/consent-management/page.tsx +576 -0
- package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
- package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
- package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
- package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
- package/src/app/docs/components/hooks/page.tsx +305 -0
- package/src/app/docs/components/page.tsx +84 -0
- package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
- package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
- package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
- package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
- package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
- package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
- package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
- package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
- package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
- package/src/app/docs/guides/managing-consent/page.tsx +738 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
- package/src/app/docs/guides/page.tsx +67 -0
- package/src/app/docs/layout.tsx +15 -0
- package/src/app/docs/metadata.ts +31 -0
- package/src/app/docs/page.tsx +572 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +123 -0
- package/src/app/layout.tsx +37 -0
- package/src/app/ndpr-demos/breach/page.tsx +354 -0
- package/src/app/ndpr-demos/consent/page.tsx +366 -0
- package/src/app/ndpr-demos/dpia/page.tsx +495 -0
- package/src/app/ndpr-demos/dsr/page.tsx +280 -0
- package/src/app/ndpr-demos/page.tsx +73 -0
- package/src/app/ndpr-demos/policy/page.tsx +771 -0
- package/src/app/page.tsx +452 -0
- package/src/components/ErrorBoundary.tsx +90 -0
- package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
- package/src/components/consent/ConsentBanner.tsx +159 -0
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
- package/src/components/docs/DocLayout.tsx +289 -0
- package/src/components/docs/index.ts +2 -0
- package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
- package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
- package/src/components/privacy-policy/data.ts +98 -0
- package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
- package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
- package/src/components/privacy-policy/shared/FormField.tsx +79 -0
- package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
- package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
- package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
- package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
- package/src/components/ui/Badge.tsx +46 -0
- package/src/components/ui/Button.tsx +59 -0
- package/src/components/ui/Card.tsx +92 -0
- package/src/components/ui/Checkbox.tsx +57 -0
- package/src/components/ui/FormField.tsx +50 -0
- package/src/components/ui/Input.tsx +38 -0
- package/src/components/ui/Loading.tsx +201 -0
- package/src/components/ui/Select.tsx +42 -0
- package/src/components/ui/TextArea.tsx +38 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/hooks/useConsent.ts +64 -0
- package/src/hooks/useLoadingState.ts +85 -0
- package/src/lib/consentService.ts +137 -0
- package/src/lib/dpiaQuestions.ts +148 -0
- package/src/lib/requestService.ts +75 -0
- package/src/lib/sanitize.ts +108 -0
- package/src/lib/storage.ts +222 -0
- package/src/lib/utils.ts +6 -0
- package/src/types/html-to-docx.d.ts +30 -0
- package/src/types/index.ts +72 -0
- package/tailwind.config.ts +65 -0
- package/tsconfig.json +41 -0
- package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
- package/dist/components/breach/BreachReportForm.d.ts +0 -66
- package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
- package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
- package/dist/components/consent/ConsentBanner.d.ts +0 -79
- package/dist/components/consent/ConsentManager.d.ts +0 -73
- package/dist/components/consent/ConsentStorage.d.ts +0 -41
- package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
- package/dist/components/dpia/DPIAReport.d.ts +0 -40
- package/dist/components/dpia/StepIndicator.d.ts +0 -64
- package/dist/components/dsr/DSRDashboard.d.ts +0 -58
- package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
- package/dist/components/dsr/DSRTracker.d.ts +0 -56
- package/dist/components/policy/PolicyExporter.d.ts +0 -65
- package/dist/components/policy/PolicyGenerator.d.ts +0 -54
- package/dist/components/policy/PolicyPreview.d.ts +0 -71
- package/dist/hooks/useBreach.d.ts +0 -97
- package/dist/hooks/useConsent.d.ts +0 -63
- package/dist/hooks/useDPIA.d.ts +0 -92
- package/dist/hooks/useDSR.d.ts +0 -72
- package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
- package/dist/index.esm.js +0 -2
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/setupTests.d.ts +0 -2
- package/dist/types/breach.d.ts +0 -239
- package/dist/types/consent.d.ts +0 -95
- package/dist/types/dpia.d.ts +0 -196
- package/dist/types/dsr.d.ts +0 -162
- package/dist/types/privacy.d.ts +0 -204
- package/dist/utils/breach.d.ts +0 -14
- package/dist/utils/consent.d.ts +0 -10
- package/dist/utils/dpia.d.ts +0 -12
- package/dist/utils/dsr.d.ts +0 -11
- package/dist/utils/privacy.d.ts +0 -12
|
@@ -0,0 +1,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(/ /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
|
+
}
|