@tantainnovative/ndpr-toolkit 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/.eslintrc.json +10 -0
  3. package/.github/workflows/ci.yml +36 -0
  4. package/.github/workflows/nextjs.yml +104 -0
  5. package/.husky/commit-msg +4 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.lintstagedrc.js +4 -0
  8. package/.nvmrc +1 -0
  9. package/.versionrc +17 -0
  10. package/CHANGELOG.md +16 -0
  11. package/CLAUDE.md +90 -0
  12. package/CNAME +1 -0
  13. package/CONTRIBUTING.md +87 -0
  14. package/README.md +84 -431
  15. package/RELEASE-NOTES-v1.0.0.md +140 -0
  16. package/RELEASE-NOTES-v1.0.1.md +69 -0
  17. package/SECURITY.md +21 -0
  18. package/commitlint.config.js +36 -0
  19. package/components.json +21 -0
  20. package/eslint.config.mjs +16 -0
  21. package/jest.config.js +31 -0
  22. package/jest.setup.js +15 -0
  23. package/next.config.js +15 -0
  24. package/next.config.ts +62 -0
  25. package/package.json +70 -52
  26. package/packages/ndpr-toolkit/README.md +467 -0
  27. package/packages/ndpr-toolkit/jest.config.js +23 -0
  28. package/packages/ndpr-toolkit/package-lock.json +8197 -0
  29. package/packages/ndpr-toolkit/package.json +71 -0
  30. package/packages/ndpr-toolkit/rollup.config.js +34 -0
  31. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
  32. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
  33. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
  34. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
  35. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
  36. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
  37. package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
  38. package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
  39. package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
  40. package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
  41. package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
  42. package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
  43. package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
  44. package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
  45. package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
  46. package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
  47. package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
  48. package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
  49. package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
  50. package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
  51. package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
  52. package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
  53. package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
  54. package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
  55. package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
  56. package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
  57. package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
  58. package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
  59. package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
  60. package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
  61. package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
  62. package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
  63. package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
  64. package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
  65. package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +14 -1
  66. package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
  67. package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
  68. package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
  69. package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
  70. package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
  71. package/packages/ndpr-toolkit/src/types/index.ts +42 -0
  72. package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
  73. package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
  74. package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
  75. package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
  76. package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
  77. package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
  78. package/packages/ndpr-toolkit/tsconfig.json +23 -0
  79. package/postcss.config.mjs +5 -0
  80. package/public/NDPR TOOLKIT.svg +1 -0
  81. package/public/favicon/android-chrome-192x192.png +0 -0
  82. package/public/favicon/android-chrome-512x512.png +0 -0
  83. package/public/favicon/apple-touch-icon.png +0 -0
  84. package/public/favicon/favicon-16x16.png +0 -0
  85. package/public/favicon/favicon-32x32.png +0 -0
  86. package/public/favicon/site.webmanifest +1 -0
  87. package/public/file.svg +1 -0
  88. package/public/globe.svg +1 -0
  89. package/public/ndpr-toolkit-logo.svg +108 -0
  90. package/public/next.svg +1 -0
  91. package/public/vercel.svg +1 -0
  92. package/public/window.svg +1 -0
  93. package/src/__tests__/example.test.ts +13 -0
  94. package/src/__tests__/requestService.test.ts +57 -0
  95. package/src/app/accessibility.css +70 -0
  96. package/src/app/docs/components/DocLayout.tsx +267 -0
  97. package/src/app/docs/components/breach-notification/page.tsx +797 -0
  98. package/src/app/docs/components/consent-management/page.tsx +576 -0
  99. package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
  100. package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
  101. package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
  102. package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
  103. package/src/app/docs/components/hooks/page.tsx +305 -0
  104. package/src/app/docs/components/page.tsx +84 -0
  105. package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
  106. package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
  107. package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
  108. package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
  109. package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
  110. package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
  111. package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
  112. package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
  113. package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
  114. package/src/app/docs/guides/managing-consent/page.tsx +738 -0
  115. package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
  116. package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
  117. package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
  118. package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
  119. package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
  120. package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
  121. package/src/app/docs/guides/page.tsx +67 -0
  122. package/src/app/docs/layout.tsx +15 -0
  123. package/src/app/docs/metadata.ts +31 -0
  124. package/src/app/docs/page.tsx +572 -0
  125. package/src/app/favicon.ico +0 -0
  126. package/src/app/globals.css +123 -0
  127. package/src/app/layout.tsx +37 -0
  128. package/src/app/ndpr-demos/breach/page.tsx +354 -0
  129. package/src/app/ndpr-demos/consent/page.tsx +366 -0
  130. package/src/app/ndpr-demos/dpia/page.tsx +495 -0
  131. package/src/app/ndpr-demos/dsr/page.tsx +280 -0
  132. package/src/app/ndpr-demos/page.tsx +73 -0
  133. package/src/app/ndpr-demos/policy/page.tsx +771 -0
  134. package/src/app/page.tsx +452 -0
  135. package/src/components/ErrorBoundary.tsx +90 -0
  136. package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
  137. package/src/components/consent/ConsentBanner.tsx +159 -0
  138. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
  139. package/src/components/docs/DocLayout.tsx +289 -0
  140. package/src/components/docs/index.ts +2 -0
  141. package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
  142. package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
  143. package/src/components/privacy-policy/data.ts +98 -0
  144. package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
  145. package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
  146. package/src/components/privacy-policy/shared/FormField.tsx +79 -0
  147. package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
  148. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
  149. package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
  150. package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
  151. package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
  152. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
  153. package/src/components/ui/Badge.tsx +46 -0
  154. package/src/components/ui/Button.tsx +59 -0
  155. package/src/components/ui/Card.tsx +92 -0
  156. package/src/components/ui/Checkbox.tsx +57 -0
  157. package/src/components/ui/FormField.tsx +50 -0
  158. package/src/components/ui/Input.tsx +38 -0
  159. package/src/components/ui/Loading.tsx +201 -0
  160. package/src/components/ui/Select.tsx +42 -0
  161. package/src/components/ui/TextArea.tsx +38 -0
  162. package/src/components/ui/label.tsx +24 -0
  163. package/src/components/ui/switch.tsx +31 -0
  164. package/src/components/ui/tabs.tsx +66 -0
  165. package/src/hooks/useConsent.ts +64 -0
  166. package/src/hooks/useLoadingState.ts +85 -0
  167. package/src/lib/consentService.ts +137 -0
  168. package/src/lib/dpiaQuestions.ts +148 -0
  169. package/src/lib/requestService.ts +75 -0
  170. package/src/lib/sanitize.ts +108 -0
  171. package/src/lib/storage.ts +222 -0
  172. package/src/lib/utils.ts +6 -0
  173. package/src/types/html-to-docx.d.ts +30 -0
  174. package/src/types/index.ts +72 -0
  175. package/tailwind.config.ts +65 -0
  176. package/tsconfig.json +41 -0
  177. package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
  178. package/dist/components/breach/BreachReportForm.d.ts +0 -66
  179. package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
  180. package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
  181. package/dist/components/consent/ConsentBanner.d.ts +0 -79
  182. package/dist/components/consent/ConsentManager.d.ts +0 -73
  183. package/dist/components/consent/ConsentStorage.d.ts +0 -41
  184. package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
  185. package/dist/components/dpia/DPIAReport.d.ts +0 -40
  186. package/dist/components/dpia/StepIndicator.d.ts +0 -64
  187. package/dist/components/dsr/DSRDashboard.d.ts +0 -58
  188. package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
  189. package/dist/components/dsr/DSRTracker.d.ts +0 -56
  190. package/dist/components/policy/PolicyExporter.d.ts +0 -65
  191. package/dist/components/policy/PolicyGenerator.d.ts +0 -54
  192. package/dist/components/policy/PolicyPreview.d.ts +0 -71
  193. package/dist/hooks/useBreach.d.ts +0 -97
  194. package/dist/hooks/useConsent.d.ts +0 -63
  195. package/dist/hooks/useDPIA.d.ts +0 -92
  196. package/dist/hooks/useDSR.d.ts +0 -72
  197. package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
  198. package/dist/index.esm.js +0 -2
  199. package/dist/index.esm.js.map +0 -1
  200. package/dist/index.js +0 -2
  201. package/dist/index.js.map +0 -1
  202. package/dist/setupTests.d.ts +0 -2
  203. package/dist/types/breach.d.ts +0 -239
  204. package/dist/types/consent.d.ts +0 -95
  205. package/dist/types/dpia.d.ts +0 -196
  206. package/dist/types/dsr.d.ts +0 -162
  207. package/dist/types/privacy.d.ts +0 -204
  208. package/dist/utils/breach.d.ts +0 -14
  209. package/dist/utils/consent.d.ts +0 -10
  210. package/dist/utils/dpia.d.ts +0 -12
  211. package/dist/utils/dsr.d.ts +0 -11
  212. package/dist/utils/privacy.d.ts +0 -12
@@ -0,0 +1,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
+ };