@tantainnovative/ndpr-toolkit 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -431
- 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} +14 -1
- 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,496 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { BreachReport, RiskAssessment, RegulatoryNotification } from '../../types/breach';
|
|
3
|
+
|
|
4
|
+
export interface OrganizationInfo {
|
|
5
|
+
/**
|
|
6
|
+
* Name of the organization
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registration number or business ID
|
|
12
|
+
*/
|
|
13
|
+
registrationNumber?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Physical address of the organization
|
|
17
|
+
*/
|
|
18
|
+
address: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Website URL
|
|
22
|
+
*/
|
|
23
|
+
website?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Name of the Data Protection Officer
|
|
27
|
+
*/
|
|
28
|
+
dpoName: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Email of the Data Protection Officer
|
|
32
|
+
*/
|
|
33
|
+
dpoEmail: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Phone number of the Data Protection Officer
|
|
37
|
+
*/
|
|
38
|
+
dpoPhone?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RegulatoryReportGeneratorProps {
|
|
42
|
+
/**
|
|
43
|
+
* The breach data to include in the report
|
|
44
|
+
*/
|
|
45
|
+
breachData: BreachReport;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The risk assessment data
|
|
49
|
+
*/
|
|
50
|
+
assessmentData?: RiskAssessment;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Organization information to include in the report
|
|
54
|
+
*/
|
|
55
|
+
organizationInfo: OrganizationInfo;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Callback function called when the report is generated
|
|
59
|
+
*/
|
|
60
|
+
onGenerate: (report: RegulatoryNotification) => void;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Title displayed on the generator form
|
|
64
|
+
* @default "Generate NITDA Notification Report"
|
|
65
|
+
*/
|
|
66
|
+
title?: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Description text displayed on the generator form
|
|
70
|
+
* @default "Generate a report for submission to NITDA in compliance with the NDPR breach notification requirements."
|
|
71
|
+
*/
|
|
72
|
+
description?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Text for the generate button
|
|
76
|
+
* @default "Generate Report"
|
|
77
|
+
*/
|
|
78
|
+
generateButtonText?: string;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Custom CSS class for the form
|
|
82
|
+
*/
|
|
83
|
+
className?: string;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Custom CSS class for the buttons
|
|
87
|
+
*/
|
|
88
|
+
buttonClassName?: string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Whether to show a preview of the generated report
|
|
92
|
+
* @default true
|
|
93
|
+
*/
|
|
94
|
+
showPreview?: boolean;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether to allow editing the report content
|
|
98
|
+
* @default true
|
|
99
|
+
*/
|
|
100
|
+
allowEditing?: boolean;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Whether to allow downloading the report
|
|
104
|
+
* @default true
|
|
105
|
+
*/
|
|
106
|
+
allowDownload?: boolean;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Format for downloading the report
|
|
110
|
+
* @default "pdf"
|
|
111
|
+
*/
|
|
112
|
+
downloadFormat?: 'pdf' | 'docx' | 'html';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const RegulatoryReportGenerator: React.FC<RegulatoryReportGeneratorProps> = ({
|
|
116
|
+
breachData,
|
|
117
|
+
assessmentData,
|
|
118
|
+
organizationInfo,
|
|
119
|
+
onGenerate,
|
|
120
|
+
title = "Generate NITDA Notification Report",
|
|
121
|
+
description = "Generate a report for submission to NITDA in compliance with the NDPR breach notification requirements.",
|
|
122
|
+
generateButtonText = "Generate Report",
|
|
123
|
+
className = "",
|
|
124
|
+
buttonClassName = "",
|
|
125
|
+
showPreview = true,
|
|
126
|
+
allowEditing = true,
|
|
127
|
+
allowDownload = true,
|
|
128
|
+
downloadFormat = "pdf"
|
|
129
|
+
}) => {
|
|
130
|
+
// Form state
|
|
131
|
+
const [reportContent, setReportContent] = useState<string>("");
|
|
132
|
+
const [contactName, setContactName] = useState<string>("");
|
|
133
|
+
const [contactEmail, setContactEmail] = useState<string>("");
|
|
134
|
+
const [contactPhone, setContactPhone] = useState<string>("");
|
|
135
|
+
const [method, setMethod] = useState<'email' | 'portal' | 'letter' | 'other'>('email');
|
|
136
|
+
const [referenceNumber, setReferenceNumber] = useState<string>("");
|
|
137
|
+
const [additionalInfo, setAdditionalInfo] = useState<string>("");
|
|
138
|
+
const [isGenerated, setIsGenerated] = useState<boolean>(false);
|
|
139
|
+
const [isSubmitted, setIsSubmitted] = useState<boolean>(false);
|
|
140
|
+
|
|
141
|
+
// Generate the initial report content
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!isGenerated) {
|
|
144
|
+
const initialContent = generateInitialContent();
|
|
145
|
+
setReportContent(initialContent);
|
|
146
|
+
setIsGenerated(true);
|
|
147
|
+
}
|
|
148
|
+
}, [breachData, assessmentData, organizationInfo]);
|
|
149
|
+
|
|
150
|
+
// Format a date from timestamp
|
|
151
|
+
const formatDate = (timestamp: number): string => {
|
|
152
|
+
return new Date(timestamp).toLocaleDateString('en-GB', {
|
|
153
|
+
day: 'numeric',
|
|
154
|
+
month: 'long',
|
|
155
|
+
year: 'numeric'
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Generate the initial report content
|
|
160
|
+
const generateInitialContent = (): string => {
|
|
161
|
+
const now = new Date();
|
|
162
|
+
const formattedDate = now.toLocaleDateString('en-GB', {
|
|
163
|
+
day: 'numeric',
|
|
164
|
+
month: 'long',
|
|
165
|
+
year: 'numeric'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return `
|
|
169
|
+
NITDA DATA BREACH NOTIFICATION
|
|
170
|
+
|
|
171
|
+
Date: ${formattedDate}
|
|
172
|
+
|
|
173
|
+
ORGANIZATION DETAILS
|
|
174
|
+
-------------------
|
|
175
|
+
Organization Name: ${organizationInfo.name}
|
|
176
|
+
${organizationInfo.registrationNumber ? `Registration Number: ${organizationInfo.registrationNumber}` : ''}
|
|
177
|
+
Address: ${organizationInfo.address}
|
|
178
|
+
${organizationInfo.website ? `Website: ${organizationInfo.website}` : ''}
|
|
179
|
+
|
|
180
|
+
DATA PROTECTION OFFICER
|
|
181
|
+
----------------------
|
|
182
|
+
Name: ${organizationInfo.dpoName}
|
|
183
|
+
Email: ${organizationInfo.dpoEmail}
|
|
184
|
+
${organizationInfo.dpoPhone ? `Phone: ${organizationInfo.dpoPhone}` : ''}
|
|
185
|
+
|
|
186
|
+
BREACH DETAILS
|
|
187
|
+
-------------
|
|
188
|
+
Breach Title: ${breachData.title}
|
|
189
|
+
Date Discovered: ${formatDate(breachData.discoveredAt)}
|
|
190
|
+
${breachData.occurredAt ? `Date Occurred: ${formatDate(breachData.occurredAt)}` : 'Date Occurred: Unknown'}
|
|
191
|
+
Status: ${breachData.status.charAt(0).toUpperCase() + breachData.status.slice(1)}
|
|
192
|
+
|
|
193
|
+
Description of the Breach:
|
|
194
|
+
${breachData.description}
|
|
195
|
+
|
|
196
|
+
Affected Systems/Applications:
|
|
197
|
+
${breachData.affectedSystems.join(', ')}
|
|
198
|
+
|
|
199
|
+
Types of Personal Data Involved:
|
|
200
|
+
${breachData.dataTypes.join(', ')}
|
|
201
|
+
|
|
202
|
+
Estimated Number of Data Subjects Affected:
|
|
203
|
+
${breachData.estimatedAffectedSubjects || 'Unknown'}
|
|
204
|
+
|
|
205
|
+
RISK ASSESSMENT
|
|
206
|
+
--------------
|
|
207
|
+
${assessmentData ? `
|
|
208
|
+
Overall Risk Level: ${assessmentData.riskLevel.charAt(0).toUpperCase() + assessmentData.riskLevel.slice(1)}
|
|
209
|
+
Risk to Rights and Freedoms of Data Subjects: ${assessmentData.risksToRightsAndFreedoms ? 'Yes' : 'No'}
|
|
210
|
+
High Risk to Rights and Freedoms of Data Subjects: ${assessmentData.highRisksToRightsAndFreedoms ? 'Yes' : 'No'}
|
|
211
|
+
|
|
212
|
+
Justification for Risk Assessment:
|
|
213
|
+
${assessmentData.justification}
|
|
214
|
+
` : 'Risk assessment has not been conducted yet.'}
|
|
215
|
+
|
|
216
|
+
MEASURES TAKEN
|
|
217
|
+
-------------
|
|
218
|
+
Measures taken or proposed to address the breach:
|
|
219
|
+
${breachData.initialActions || 'To be determined'}
|
|
220
|
+
|
|
221
|
+
Measures taken or proposed to mitigate possible adverse effects:
|
|
222
|
+
[Please specify measures taken to mitigate adverse effects]
|
|
223
|
+
|
|
224
|
+
NOTIFICATION TO DATA SUBJECTS
|
|
225
|
+
----------------------------
|
|
226
|
+
Have data subjects been notified: [Yes/No]
|
|
227
|
+
If yes, date of notification: [Date]
|
|
228
|
+
If no, planned date of notification: [Date]
|
|
229
|
+
Reason for delay (if applicable): [Reason]
|
|
230
|
+
|
|
231
|
+
ADDITIONAL INFORMATION
|
|
232
|
+
---------------------
|
|
233
|
+
[Any additional information relevant to the breach]
|
|
234
|
+
|
|
235
|
+
This notification is made in compliance with the Nigeria Data Protection Regulation (NDPR).
|
|
236
|
+
`;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Handle form submission
|
|
240
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
|
|
243
|
+
const notification: RegulatoryNotification = {
|
|
244
|
+
id: `notification_${Date.now()}`,
|
|
245
|
+
breachId: breachData.id,
|
|
246
|
+
sentAt: Date.now(),
|
|
247
|
+
method,
|
|
248
|
+
referenceNumber: referenceNumber || undefined,
|
|
249
|
+
nitdaContact: contactName ? {
|
|
250
|
+
name: contactName,
|
|
251
|
+
email: contactEmail,
|
|
252
|
+
phone: contactPhone || undefined
|
|
253
|
+
} : undefined,
|
|
254
|
+
content: reportContent,
|
|
255
|
+
attachments: []
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
onGenerate(notification);
|
|
259
|
+
setIsSubmitted(true);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Handle download
|
|
263
|
+
const handleDownload = () => {
|
|
264
|
+
// In a real implementation, this would generate a PDF, DOCX, or HTML file
|
|
265
|
+
// For this example, we'll just create a text file
|
|
266
|
+
|
|
267
|
+
const element = document.createElement('a');
|
|
268
|
+
const file = new Blob([reportContent], {type: 'text/plain'});
|
|
269
|
+
element.href = URL.createObjectURL(file);
|
|
270
|
+
element.download = `NITDA_Breach_Notification_${new Date().toISOString().split('T')[0]}.txt`;
|
|
271
|
+
document.body.appendChild(element);
|
|
272
|
+
element.click();
|
|
273
|
+
document.body.removeChild(element);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Render the notification method options
|
|
277
|
+
const renderMethodOptions = () => {
|
|
278
|
+
const options = [
|
|
279
|
+
{ value: 'email', label: 'Email' },
|
|
280
|
+
{ value: 'portal', label: 'NITDA Portal' },
|
|
281
|
+
{ value: 'letter', label: 'Formal Letter' },
|
|
282
|
+
{ value: 'other', label: 'Other' }
|
|
283
|
+
];
|
|
284
|
+
|
|
285
|
+
return options.map(option => (
|
|
286
|
+
<option key={option.value} value={option.value}>
|
|
287
|
+
{option.label}
|
|
288
|
+
</option>
|
|
289
|
+
));
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<div className={`bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md ${className}`}>
|
|
294
|
+
<h2 className="text-xl font-bold mb-2">{title}</h2>
|
|
295
|
+
<p className="mb-6 text-gray-600 dark:text-gray-300">{description}</p>
|
|
296
|
+
|
|
297
|
+
{isSubmitted ? (
|
|
298
|
+
<div>
|
|
299
|
+
<div className="mb-6 p-4 bg-green-50 dark:bg-green-900/20 rounded-md">
|
|
300
|
+
<h3 className="text-lg font-bold text-green-800 dark:text-green-200 mb-2">Report Generated Successfully</h3>
|
|
301
|
+
<p className="text-green-700 dark:text-green-300">
|
|
302
|
+
Your NITDA notification report has been generated and is ready for submission.
|
|
303
|
+
Please review the report carefully before submitting it to NITDA.
|
|
304
|
+
</p>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div className="mb-6">
|
|
308
|
+
<h3 className="text-lg font-semibold mb-3">Submission Details</h3>
|
|
309
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
310
|
+
<div>
|
|
311
|
+
<p className="text-sm"><span className="font-medium">Method:</span> {method.charAt(0).toUpperCase() + method.slice(1)}</p>
|
|
312
|
+
{contactName && <p className="text-sm"><span className="font-medium">Contact Name:</span> {contactName}</p>}
|
|
313
|
+
{contactEmail && <p className="text-sm"><span className="font-medium">Contact Email:</span> {contactEmail}</p>}
|
|
314
|
+
{contactPhone && <p className="text-sm"><span className="font-medium">Contact Phone:</span> {contactPhone}</p>}
|
|
315
|
+
</div>
|
|
316
|
+
<div>
|
|
317
|
+
<p className="text-sm"><span className="font-medium">Date Generated:</span> {formatDate(Date.now())}</p>
|
|
318
|
+
<p className="text-sm"><span className="font-medium">Breach ID:</span> {breachData.id}</p>
|
|
319
|
+
{referenceNumber && <p className="text-sm"><span className="font-medium">Reference Number:</span> {referenceNumber}</p>}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
{showPreview && (
|
|
325
|
+
<div className="mb-6">
|
|
326
|
+
<h3 className="text-lg font-semibold mb-3">Report Preview</h3>
|
|
327
|
+
<div className="bg-gray-50 dark:bg-gray-700 p-4 rounded-md">
|
|
328
|
+
<pre className="whitespace-pre-wrap text-sm font-mono text-gray-800 dark:text-gray-200">
|
|
329
|
+
{reportContent}
|
|
330
|
+
</pre>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
|
|
335
|
+
<div className="flex flex-wrap gap-3">
|
|
336
|
+
{allowDownload && (
|
|
337
|
+
<button
|
|
338
|
+
onClick={handleDownload}
|
|
339
|
+
className={`px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 ${buttonClassName}`}
|
|
340
|
+
>
|
|
341
|
+
Download Report ({downloadFormat.toUpperCase()})
|
|
342
|
+
</button>
|
|
343
|
+
)}
|
|
344
|
+
<button
|
|
345
|
+
onClick={() => setIsSubmitted(false)}
|
|
346
|
+
className={`px-4 py-2 bg-gray-200 text-gray-800 dark:bg-gray-700 dark:text-white rounded hover:bg-gray-300 dark:hover:bg-gray-600 ${buttonClassName}`}
|
|
347
|
+
>
|
|
348
|
+
Edit Report
|
|
349
|
+
</button>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
) : (
|
|
353
|
+
<form onSubmit={handleSubmit}>
|
|
354
|
+
<div className="space-y-6">
|
|
355
|
+
{/* Notification Method */}
|
|
356
|
+
<div>
|
|
357
|
+
<h3 className="text-lg font-semibold mb-3">Notification Method</h3>
|
|
358
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
359
|
+
<div>
|
|
360
|
+
<label htmlFor="method" className="block text-sm font-medium mb-1">
|
|
361
|
+
Method of Submission <span className="text-red-500">*</span>
|
|
362
|
+
</label>
|
|
363
|
+
<select
|
|
364
|
+
id="method"
|
|
365
|
+
value={method}
|
|
366
|
+
onChange={e => setMethod(e.target.value as 'email' | 'portal' | 'letter' | 'other')}
|
|
367
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
368
|
+
required
|
|
369
|
+
>
|
|
370
|
+
{renderMethodOptions()}
|
|
371
|
+
</select>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<div>
|
|
375
|
+
<label htmlFor="referenceNumber" className="block text-sm font-medium mb-1">
|
|
376
|
+
Reference Number (if available)
|
|
377
|
+
</label>
|
|
378
|
+
<input
|
|
379
|
+
type="text"
|
|
380
|
+
id="referenceNumber"
|
|
381
|
+
value={referenceNumber}
|
|
382
|
+
onChange={e => setReferenceNumber(e.target.value)}
|
|
383
|
+
placeholder="e.g. NITDA/BR/2023/001"
|
|
384
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
385
|
+
/>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
{/* NITDA Contact */}
|
|
391
|
+
<div>
|
|
392
|
+
<h3 className="text-lg font-semibold mb-3">NITDA Contact (if known)</h3>
|
|
393
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
|
394
|
+
<div>
|
|
395
|
+
<label htmlFor="contactName" className="block text-sm font-medium mb-1">
|
|
396
|
+
Contact Name
|
|
397
|
+
</label>
|
|
398
|
+
<input
|
|
399
|
+
type="text"
|
|
400
|
+
id="contactName"
|
|
401
|
+
value={contactName}
|
|
402
|
+
onChange={e => setContactName(e.target.value)}
|
|
403
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
|
|
407
|
+
<div>
|
|
408
|
+
<label htmlFor="contactEmail" className="block text-sm font-medium mb-1">
|
|
409
|
+
Contact Email
|
|
410
|
+
</label>
|
|
411
|
+
<input
|
|
412
|
+
type="email"
|
|
413
|
+
id="contactEmail"
|
|
414
|
+
value={contactEmail}
|
|
415
|
+
onChange={e => setContactEmail(e.target.value)}
|
|
416
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<div>
|
|
421
|
+
<label htmlFor="contactPhone" className="block text-sm font-medium mb-1">
|
|
422
|
+
Contact Phone
|
|
423
|
+
</label>
|
|
424
|
+
<input
|
|
425
|
+
type="tel"
|
|
426
|
+
id="contactPhone"
|
|
427
|
+
value={contactPhone}
|
|
428
|
+
onChange={e => setContactPhone(e.target.value)}
|
|
429
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
430
|
+
/>
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
|
|
435
|
+
{/* Additional Information */}
|
|
436
|
+
<div>
|
|
437
|
+
<h3 className="text-lg font-semibold mb-3">Additional Information</h3>
|
|
438
|
+
<div>
|
|
439
|
+
<label htmlFor="additionalInfo" className="block text-sm font-medium mb-1">
|
|
440
|
+
Additional Information to Include
|
|
441
|
+
</label>
|
|
442
|
+
<textarea
|
|
443
|
+
id="additionalInfo"
|
|
444
|
+
value={additionalInfo}
|
|
445
|
+
onChange={e => setAdditionalInfo(e.target.value)}
|
|
446
|
+
rows={3}
|
|
447
|
+
placeholder="Any additional information you want to include in the report"
|
|
448
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
449
|
+
/>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* Report Content */}
|
|
454
|
+
{allowEditing && (
|
|
455
|
+
<div>
|
|
456
|
+
<h3 className="text-lg font-semibold mb-3">Report Content</h3>
|
|
457
|
+
<div>
|
|
458
|
+
<label htmlFor="reportContent" className="block text-sm font-medium mb-1">
|
|
459
|
+
Edit Report Content <span className="text-red-500">*</span>
|
|
460
|
+
</label>
|
|
461
|
+
<textarea
|
|
462
|
+
id="reportContent"
|
|
463
|
+
value={reportContent}
|
|
464
|
+
onChange={e => setReportContent(e.target.value)}
|
|
465
|
+
rows={20}
|
|
466
|
+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm"
|
|
467
|
+
required
|
|
468
|
+
/>
|
|
469
|
+
</div>
|
|
470
|
+
</div>
|
|
471
|
+
)}
|
|
472
|
+
|
|
473
|
+
{/* NDPR Notice */}
|
|
474
|
+
<div className="mt-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-md">
|
|
475
|
+
<h3 className="text-sm font-bold text-blue-800 dark:text-blue-200 mb-2">NDPR Breach Notification Requirements</h3>
|
|
476
|
+
<p className="text-blue-700 dark:text-blue-300 text-sm">
|
|
477
|
+
Under the Nigeria Data Protection Regulation (NDPR), data breaches that pose a risk to the rights and freedoms of data subjects must be reported to NITDA within 72 hours of discovery.
|
|
478
|
+
This report will help you comply with this requirement.
|
|
479
|
+
</p>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
{/* Submit Button */}
|
|
483
|
+
<div className="mt-6">
|
|
484
|
+
<button
|
|
485
|
+
type="submit"
|
|
486
|
+
className={`px-6 py-3 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${buttonClassName}`}
|
|
487
|
+
>
|
|
488
|
+
{generateButtonText}
|
|
489
|
+
</button>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
</form>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
495
|
+
);
|
|
496
|
+
};
|