@tantainnovative/ndpr-toolkit 1.0.3 → 1.0.4

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 (156) hide show
  1. package/next-env.d.ts +5 -0
  2. package/package.json +1 -1
  3. package/.claude/settings.local.json +0 -20
  4. package/.eslintrc.json +0 -10
  5. package/.github/workflows/ci.yml +0 -36
  6. package/.github/workflows/nextjs.yml +0 -104
  7. package/.husky/commit-msg +0 -4
  8. package/.husky/pre-commit +0 -4
  9. package/.lintstagedrc.js +0 -4
  10. package/.nvmrc +0 -1
  11. package/.versionrc +0 -17
  12. package/CLAUDE.md +0 -90
  13. package/commitlint.config.js +0 -36
  14. package/eslint.config.mjs +0 -16
  15. package/jest.config.js +0 -31
  16. package/jest.setup.js +0 -15
  17. package/next.config.js +0 -15
  18. package/next.config.ts +0 -62
  19. package/packages/ndpr-toolkit/README.md +0 -467
  20. package/packages/ndpr-toolkit/jest.config.js +0 -23
  21. package/packages/ndpr-toolkit/package-lock.json +0 -8197
  22. package/packages/ndpr-toolkit/package.json +0 -71
  23. package/packages/ndpr-toolkit/rollup.config.js +0 -34
  24. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +0 -119
  25. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +0 -122
  26. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +0 -270
  27. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +0 -199
  28. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +0 -224
  29. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +0 -104
  30. package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +0 -161
  31. package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +0 -330
  32. package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +0 -149
  33. package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +0 -88
  34. package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +0 -160
  35. package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +0 -110
  36. package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +0 -97
  37. package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +0 -701
  38. package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +0 -631
  39. package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +0 -569
  40. package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +0 -496
  41. package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +0 -270
  42. package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +0 -217
  43. package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +0 -206
  44. package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +0 -342
  45. package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +0 -373
  46. package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +0 -174
  47. package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +0 -717
  48. package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +0 -476
  49. package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +0 -620
  50. package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +0 -541
  51. package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +0 -454
  52. package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +0 -333
  53. package/packages/ndpr-toolkit/src/hooks/useBreach.ts +0 -409
  54. package/packages/ndpr-toolkit/src/hooks/useConsent.ts +0 -263
  55. package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +0 -457
  56. package/packages/ndpr-toolkit/src/hooks/useDSR.ts +0 -236
  57. package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +0 -428
  58. package/packages/ndpr-toolkit/src/index.ts +0 -44
  59. package/packages/ndpr-toolkit/src/setupTests.ts +0 -5
  60. package/packages/ndpr-toolkit/src/types/breach.ts +0 -283
  61. package/packages/ndpr-toolkit/src/types/consent.ts +0 -111
  62. package/packages/ndpr-toolkit/src/types/dpia.ts +0 -236
  63. package/packages/ndpr-toolkit/src/types/dsr.ts +0 -192
  64. package/packages/ndpr-toolkit/src/types/index.ts +0 -42
  65. package/packages/ndpr-toolkit/src/types/privacy.ts +0 -246
  66. package/packages/ndpr-toolkit/src/utils/breach.ts +0 -122
  67. package/packages/ndpr-toolkit/src/utils/consent.ts +0 -51
  68. package/packages/ndpr-toolkit/src/utils/dpia.ts +0 -104
  69. package/packages/ndpr-toolkit/src/utils/dsr.ts +0 -77
  70. package/packages/ndpr-toolkit/src/utils/privacy.ts +0 -100
  71. package/packages/ndpr-toolkit/tsconfig.json +0 -23
  72. package/postcss.config.mjs +0 -5
  73. package/src/__tests__/example.test.ts +0 -13
  74. package/src/__tests__/requestService.test.ts +0 -57
  75. package/src/app/accessibility.css +0 -70
  76. package/src/app/docs/components/DocLayout.tsx +0 -267
  77. package/src/app/docs/components/breach-notification/page.tsx +0 -797
  78. package/src/app/docs/components/consent-management/page.tsx +0 -576
  79. package/src/app/docs/components/data-subject-rights/page.tsx +0 -511
  80. package/src/app/docs/components/dpia-questionnaire/layout.tsx +0 -15
  81. package/src/app/docs/components/dpia-questionnaire/metadata.ts +0 -31
  82. package/src/app/docs/components/dpia-questionnaire/page.tsx +0 -666
  83. package/src/app/docs/components/hooks/page.tsx +0 -305
  84. package/src/app/docs/components/page.tsx +0 -84
  85. package/src/app/docs/components/privacy-policy-generator/page.tsx +0 -634
  86. package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +0 -123
  87. package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +0 -328
  88. package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +0 -28
  89. package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +0 -91
  90. package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +0 -118
  91. package/src/app/docs/guides/breach-notification-process/page.tsx +0 -39
  92. package/src/app/docs/guides/conducting-dpia/page.tsx +0 -593
  93. package/src/app/docs/guides/data-subject-requests/page.tsx +0 -666
  94. package/src/app/docs/guides/managing-consent/page.tsx +0 -738
  95. package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +0 -296
  96. package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +0 -145
  97. package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +0 -33
  98. package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +0 -99
  99. package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +0 -159
  100. package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +0 -38
  101. package/src/app/docs/guides/page.tsx +0 -67
  102. package/src/app/docs/layout.tsx +0 -15
  103. package/src/app/docs/metadata.ts +0 -31
  104. package/src/app/docs/page.tsx +0 -572
  105. package/src/app/favicon.ico +0 -0
  106. package/src/app/globals.css +0 -123
  107. package/src/app/layout.tsx +0 -37
  108. package/src/app/ndpr-demos/breach/page.tsx +0 -354
  109. package/src/app/ndpr-demos/consent/page.tsx +0 -366
  110. package/src/app/ndpr-demos/dpia/page.tsx +0 -495
  111. package/src/app/ndpr-demos/dsr/page.tsx +0 -280
  112. package/src/app/ndpr-demos/page.tsx +0 -73
  113. package/src/app/ndpr-demos/policy/page.tsx +0 -771
  114. package/src/app/page.tsx +0 -452
  115. package/src/components/ErrorBoundary.tsx +0 -90
  116. package/src/components/breach-notification/BreachNotificationForm.tsx +0 -479
  117. package/src/components/consent/ConsentBanner.tsx +0 -159
  118. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +0 -419
  119. package/src/components/docs/DocLayout.tsx +0 -289
  120. package/src/components/docs/index.ts +0 -2
  121. package/src/components/dpia/DPIAQuestionnaire.tsx +0 -483
  122. package/src/components/privacy-policy/PolicyGenerator.tsx +0 -1062
  123. package/src/components/privacy-policy/data.ts +0 -98
  124. package/src/components/privacy-policy/shared/CheckboxField.tsx +0 -38
  125. package/src/components/privacy-policy/shared/CheckboxGroup.tsx +0 -85
  126. package/src/components/privacy-policy/shared/FormField.tsx +0 -79
  127. package/src/components/privacy-policy/shared/StepIndicator.tsx +0 -86
  128. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +0 -335
  129. package/src/components/privacy-policy/steps/DataCollectionStep.tsx +0 -231
  130. package/src/components/privacy-policy/steps/DataSharingStep.tsx +0 -418
  131. package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +0 -202
  132. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +0 -172
  133. package/src/components/ui/Badge.tsx +0 -46
  134. package/src/components/ui/Button.tsx +0 -59
  135. package/src/components/ui/Card.tsx +0 -92
  136. package/src/components/ui/Checkbox.tsx +0 -57
  137. package/src/components/ui/FormField.tsx +0 -50
  138. package/src/components/ui/Input.tsx +0 -38
  139. package/src/components/ui/Loading.tsx +0 -201
  140. package/src/components/ui/Select.tsx +0 -42
  141. package/src/components/ui/TextArea.tsx +0 -38
  142. package/src/components/ui/label.tsx +0 -24
  143. package/src/components/ui/switch.tsx +0 -31
  144. package/src/components/ui/tabs.tsx +0 -66
  145. package/src/hooks/useConsent.ts +0 -64
  146. package/src/hooks/useLoadingState.ts +0 -85
  147. package/src/lib/consentService.ts +0 -137
  148. package/src/lib/dpiaQuestions.ts +0 -148
  149. package/src/lib/requestService.ts +0 -75
  150. package/src/lib/sanitize.ts +0 -108
  151. package/src/lib/storage.ts +0 -222
  152. package/src/lib/utils.ts +0 -6
  153. package/src/types/html-to-docx.d.ts +0 -30
  154. package/src/types/index.ts +0 -72
  155. package/tailwind.config.ts +0 -65
  156. package/tsconfig.json +0 -41
@@ -1,479 +0,0 @@
1
- 'use client';
2
-
3
- import { useState } from 'react';
4
- import { BreachSeverity } from '@/types';
5
- import { Input } from '@/components/ui/Input';
6
- import { TextArea } from '@/components/ui/TextArea';
7
- import { Button } from '@/components/ui/Button';
8
- import { FormField } from '@/components/ui/FormField';
9
- import { Checkbox } from '@/components/ui/Checkbox';
10
-
11
- interface BreachNotificationFormProps {
12
- onSubmit: (data: {
13
- title: string;
14
- description: string;
15
- discoveryDate: string;
16
- affectedDataSubjects: number;
17
- dataCategories: string[];
18
- severity: BreachSeverity;
19
- mitigationSteps: string[];
20
- reportedToAuthorities: boolean;
21
- reportedToDataSubjects: boolean;
22
- }) => void;
23
- className?: string;
24
- }
25
-
26
- export default function BreachNotificationForm({
27
- onSubmit,
28
- className = '',
29
- }: BreachNotificationFormProps) {
30
- const [formData, setFormData] = useState({
31
- title: '',
32
- description: '',
33
- discoveryDate: '',
34
- affectedDataSubjects: 0,
35
- dataCategories: [] as string[],
36
- severity: 'medium' as BreachSeverity,
37
- mitigationSteps: [] as string[],
38
- reportedToAuthorities: false,
39
- reportedToDataSubjects: false,
40
- });
41
-
42
- const [errors, setErrors] = useState<Record<string, string>>({});
43
- const [isSubmitting, setIsSubmitting] = useState(false);
44
- const [newMitigationStep, setNewMitigationStep] = useState('');
45
- const [newDataCategory, setNewDataCategory] = useState('');
46
-
47
- // Default data categories
48
- const defaultDataCategories = [
49
- 'Personal identifiers (name, ID numbers)',
50
- 'Contact information',
51
- 'Financial information',
52
- 'Health information',
53
- 'Biometric data',
54
- 'Location data',
55
- 'Online identifiers (IP address, cookies)',
56
- 'Credentials (usernames, passwords)',
57
- ];
58
-
59
- const handleChange = (
60
- e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
61
- ) => {
62
- const { name, value, type } = e.target as HTMLInputElement;
63
-
64
- if (type === 'checkbox') {
65
- setFormData((prev) => ({
66
- ...prev,
67
- [name]: (e.target as HTMLInputElement).checked,
68
- }));
69
- } else if (type === 'number') {
70
- setFormData((prev) => ({
71
- ...prev,
72
- [name]: parseInt(value) || 0,
73
- }));
74
- } else {
75
- setFormData((prev) => ({
76
- ...prev,
77
- [name]: value,
78
- }));
79
- }
80
-
81
- // Clear error when field is edited
82
- if (errors[name]) {
83
- setErrors((prev) => {
84
- const newErrors = { ...prev };
85
- delete newErrors[name];
86
- return newErrors;
87
- });
88
- }
89
- };
90
-
91
- const handleDataCategoryToggle = (category: string) => {
92
- setFormData((prev) => {
93
- const updatedCategories = prev.dataCategories.includes(category)
94
- ? prev.dataCategories.filter((c) => c !== category)
95
- : [...prev.dataCategories, category];
96
-
97
- return {
98
- ...prev,
99
- dataCategories: updatedCategories,
100
- };
101
- });
102
-
103
- // Clear error when field is edited
104
- if (errors.dataCategories) {
105
- setErrors((prev) => {
106
- const newErrors = { ...prev };
107
- delete newErrors.dataCategories;
108
- return newErrors;
109
- });
110
- }
111
- };
112
-
113
- const handleAddDataCategory = (e?: React.MouseEvent) => {
114
- // Prevent form submission if this is triggered by a button click
115
- if (e) {
116
- e.preventDefault();
117
- }
118
-
119
- if (newDataCategory.trim()) {
120
- // Check if category already exists to avoid duplicates
121
- if (!formData.dataCategories.includes(newDataCategory.trim())) {
122
- setFormData((prev) => ({
123
- ...prev,
124
- dataCategories: [...prev.dataCategories, newDataCategory.trim()],
125
- }));
126
- }
127
- setNewDataCategory('');
128
-
129
- // Clear error when field is edited
130
- if (errors.dataCategories) {
131
- setErrors((prev) => {
132
- const newErrors = { ...prev };
133
- delete newErrors.dataCategories;
134
- return newErrors;
135
- });
136
- }
137
- }
138
- };
139
-
140
- const handleAddMitigationStep = (e?: React.MouseEvent) => {
141
- // Prevent form submission if this is triggered by a button click
142
- if (e) {
143
- e.preventDefault();
144
- }
145
- if (newMitigationStep.trim()) {
146
- setFormData((prev) => ({
147
- ...prev,
148
- mitigationSteps: [...prev.mitigationSteps, newMitigationStep.trim()],
149
- }));
150
- setNewMitigationStep('');
151
-
152
- // Clear error when field is edited
153
- if (errors.mitigationSteps) {
154
- setErrors((prev) => {
155
- const newErrors = { ...prev };
156
- delete newErrors.mitigationSteps;
157
- return newErrors;
158
- });
159
- }
160
- }
161
- };
162
-
163
- const handleRemoveMitigationStep = (index: number) => {
164
- setFormData((prev) => ({
165
- ...prev,
166
- mitigationSteps: prev.mitigationSteps.filter((_, i) => i !== index),
167
- }));
168
- };
169
-
170
- // Handle key press for adding data category and mitigation steps
171
- const handleKeyPress = (e: React.KeyboardEvent, type: 'category' | 'mitigation') => {
172
- if (e.key === 'Enter') {
173
- e.preventDefault();
174
- if (type === 'category') {
175
- handleAddDataCategory();
176
- } else {
177
- handleAddMitigationStep();
178
- }
179
- }
180
- };
181
-
182
- const validateForm = () => {
183
- const newErrors: Record<string, string> = {};
184
-
185
- if (!formData.title.trim()) {
186
- newErrors.title = 'Title is required';
187
- }
188
-
189
- if (!formData.description.trim()) {
190
- newErrors.description = 'Description is required';
191
- }
192
-
193
- if (!formData.discoveryDate) {
194
- newErrors.discoveryDate = 'Discovery date is required';
195
- }
196
-
197
- if (formData.affectedDataSubjects <= 0) {
198
- newErrors.affectedDataSubjects = 'Number of affected data subjects must be greater than 0';
199
- }
200
-
201
- if (formData.dataCategories.length === 0) {
202
- newErrors.dataCategories = 'Please select at least one data category';
203
- }
204
-
205
- if (formData.mitigationSteps.length === 0) {
206
- newErrors.mitigationSteps = 'Please add at least one mitigation step';
207
- }
208
-
209
- setErrors(newErrors);
210
- return Object.keys(newErrors).length === 0;
211
- };
212
-
213
- const handleSubmit = async (e: React.FormEvent) => {
214
- e.preventDefault();
215
-
216
- if (!validateForm()) return;
217
-
218
- setIsSubmitting(true);
219
-
220
- try {
221
- // In a real implementation, this would call an API
222
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
223
-
224
- onSubmit({
225
- title: formData.title,
226
- description: formData.description,
227
- discoveryDate: formData.discoveryDate,
228
- affectedDataSubjects: formData.affectedDataSubjects,
229
- dataCategories: formData.dataCategories,
230
- severity: formData.severity,
231
- mitigationSteps: formData.mitigationSteps,
232
- reportedToAuthorities: formData.reportedToAuthorities,
233
- reportedToDataSubjects: formData.reportedToDataSubjects,
234
- });
235
- } catch (error) {
236
- console.error('Error submitting breach notification:', error);
237
- setErrors({
238
- submit: 'An error occurred while submitting the breach notification. Please try again.',
239
- });
240
- } finally {
241
- setIsSubmitting(false);
242
- }
243
- };
244
-
245
- return (
246
- <div className={`bg-white dark:bg-gray-800 shadow-sm rounded-lg border border-gray-200 dark:border-gray-700 p-6 ${className}`}>
247
- <h3 className="text-lg font-medium text-gray-900 dark:text-white mb-4">Data Breach Notification Form</h3>
248
-
249
- <form onSubmit={handleSubmit} className="space-y-6">
250
- <FormField
251
- id="title"
252
- label="Breach Title"
253
- required
254
- error={errors.title}
255
- >
256
- <Input
257
- id="title"
258
- name="title"
259
- value={formData.title}
260
- onChange={handleChange}
261
- placeholder="Enter title describing the breach"
262
- />
263
- </FormField>
264
-
265
- <FormField
266
- id="description"
267
- label="Breach Description"
268
- required
269
- error={errors.description}
270
- >
271
- <TextArea
272
- id="description"
273
- name="description"
274
- value={formData.description}
275
- onChange={handleChange}
276
- rows={4}
277
- placeholder="Detailed description of what happened, how it happened, and the potential impact"
278
- />
279
- </FormField>
280
-
281
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
282
- <FormField
283
- id="discoveryDate"
284
- label="Date of Discovery"
285
- required
286
- error={errors.discoveryDate}
287
- >
288
- <Input
289
- type="date"
290
- id="discoveryDate"
291
- name="discoveryDate"
292
- value={formData.discoveryDate}
293
- onChange={handleChange}
294
- max={new Date().toISOString().split('T')[0]}
295
- />
296
- </FormField>
297
-
298
- <FormField
299
- id="affectedDataSubjects"
300
- label="Number of Affected Data Subjects"
301
- required
302
- error={errors.affectedDataSubjects}
303
- >
304
- <Input
305
- type="number"
306
- id="affectedDataSubjects"
307
- name="affectedDataSubjects"
308
- value={formData.affectedDataSubjects}
309
- onChange={handleChange}
310
- min={0}
311
- />
312
- </FormField>
313
- </div>
314
-
315
- <FormField
316
- id="severity"
317
- label="Breach Severity"
318
- required
319
- error={errors.severity}
320
- >
321
- <select
322
- id="severity"
323
- name="severity"
324
- value={formData.severity}
325
- onChange={handleChange}
326
- className="block w-full px-3 py-2 text-base border border-gray-300 dark:border-gray-600 rounded-md shadow-sm bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400"
327
- >
328
- <option value="low">Low - Minimal impact on data subjects</option>
329
- <option value="medium">Medium - Moderate impact on data subjects</option>
330
- <option value="high">High - Significant impact on data subjects</option>
331
- <option value="critical">Critical - Severe impact on data subjects</option>
332
- </select>
333
- </FormField>
334
-
335
- <FormField
336
- id="dataCategories"
337
- label="Categories of Data Involved"
338
- required
339
- error={errors.dataCategories}
340
- >
341
- <div className="grid grid-cols-1 md:grid-cols-2 gap-2 mt-2">
342
- {formData.dataCategories.length > 0 && formData.dataCategories.filter(cat => !defaultDataCategories.includes(cat)).length > 0 && (
343
- <div className="col-span-2 mb-2 p-2 bg-gray-50 dark:bg-gray-800 rounded">
344
- <h4 className="text-sm font-medium mb-1">Added Categories:</h4>
345
- <div className="flex flex-wrap gap-2">
346
- {formData.dataCategories
347
- .filter(cat => !defaultDataCategories.includes(cat))
348
- .map((category, index) => (
349
- <span key={index} className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
350
- {category}
351
- <button
352
- type="button"
353
- className="ml-1 text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
354
- onClick={() => {
355
- setFormData(prev => ({
356
- ...prev,
357
- dataCategories: prev.dataCategories.filter(c => c !== category)
358
- }));
359
- }}
360
- >
361
- ×
362
- </button>
363
- </span>
364
- ))}
365
- </div>
366
- </div>
367
- )}
368
- {defaultDataCategories.map((category) => (
369
- <Checkbox
370
- key={category}
371
- id={`category-${category}`}
372
- label={category}
373
- checked={formData.dataCategories.includes(category)}
374
- onChange={() => handleDataCategoryToggle(category)}
375
- />
376
- ))}
377
- </div>
378
-
379
- <div className="flex space-x-2 mt-4">
380
- <Input
381
- type="text"
382
- id="newDataCategory"
383
- value={newDataCategory}
384
- onChange={(e) => setNewDataCategory(e.target.value)}
385
- onKeyPress={(e) => handleKeyPress(e, 'category')}
386
- placeholder="Add other data category"
387
- className="flex-1"
388
- />
389
- <Button
390
- type="button"
391
- onClick={(e) => handleAddDataCategory(e)}
392
- size="sm"
393
- >
394
- Add
395
- </Button>
396
- </div>
397
- </FormField>
398
-
399
- <FormField
400
- id="mitigationSteps"
401
- label="Mitigation Steps Taken"
402
- error={errors.mitigationSteps}
403
- >
404
- <div className="flex space-x-2 mb-2">
405
- <Input
406
- type="text"
407
- id="newMitigationStep"
408
- value={newMitigationStep}
409
- onChange={(e) => setNewMitigationStep(e.target.value)}
410
- onKeyPress={(e) => handleKeyPress(e, 'mitigation')}
411
- placeholder="e.g., Changed all passwords, Patched vulnerability"
412
- className="flex-1"
413
- />
414
- <Button
415
- type="button"
416
- onClick={(e) => handleAddMitigationStep(e)}
417
- size="sm"
418
- >
419
- Add
420
- </Button>
421
- </div>
422
-
423
- {formData.mitigationSteps.length > 0 && (
424
- <ul className="mt-2 space-y-1">
425
- {formData.mitigationSteps.map((step, index) => (
426
- <li key={index} className="flex items-center justify-between bg-gray-50 dark:bg-gray-700 p-2 rounded">
427
- <span className="text-sm text-gray-700 dark:text-gray-300">{step}</span>
428
- <Button
429
- type="button"
430
- onClick={() => handleRemoveMitigationStep(index)}
431
- variant="ghost"
432
- size="sm"
433
- className="text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300"
434
- >
435
- Remove
436
- </Button>
437
- </li>
438
- ))}
439
- </ul>
440
- )}
441
- </FormField>
442
-
443
- <div className="space-y-4">
444
- <Checkbox
445
- id="reportedToAuthorities"
446
- name="reportedToAuthorities"
447
- checked={formData.reportedToAuthorities}
448
- onChange={handleChange}
449
- label="Reported to Nigerian Data Protection Commission (NDPC)"
450
- />
451
-
452
- <Checkbox
453
- id="reportedToDataSubjects"
454
- name="reportedToDataSubjects"
455
- checked={formData.reportedToDataSubjects}
456
- onChange={handleChange}
457
- label="Reported to affected data subjects"
458
- />
459
- </div>
460
-
461
- {errors.submit && (
462
- <div className="rounded-md bg-red-50 dark:bg-red-900/20 p-4">
463
- <p className="text-sm text-red-700 dark:text-red-400">{errors.submit}</p>
464
- </div>
465
- )}
466
-
467
- <div className="flex justify-end">
468
- <Button
469
- type="submit"
470
- disabled={isSubmitting}
471
- variant="default"
472
- >
473
- {isSubmitting ? 'Submitting...' : 'Submit Breach Notification'}
474
- </Button>
475
- </div>
476
- </form>
477
- </div>
478
- );
479
- }
@@ -1,159 +0,0 @@
1
- 'use client';
2
-
3
- import { useState } from 'react';
4
- import { ConsentOption } from '@/types';
5
- import { Button } from '@/components/ui/Button';
6
- import { Checkbox } from '@/components/ui/Checkbox';
7
- import { Card, CardContent, CardFooter, CardHeader, CardTitle, CardDescription } from '@/components/ui/Card';
8
- import { Badge } from '@/components/ui/Badge';
9
-
10
- interface ConsentBannerProps {
11
- title?: string;
12
- description?: string;
13
- privacyPolicyUrl?: string;
14
- options: ConsentOption[];
15
- onSave: (consents: Record<string, boolean>) => void;
16
- onClose?: () => void;
17
- className?: string;
18
- }
19
-
20
- export default function ConsentBanner({
21
- title = 'Privacy Preferences',
22
- description = 'We use cookies and similar technologies to provide certain features, enhance the user experience and deliver content that is relevant to your interests.',
23
- privacyPolicyUrl = '/privacy-policy',
24
- options,
25
- onSave,
26
- className = '',
27
- }: ConsentBannerProps) {
28
- const [consents, setConsents] = useState<Record<string, boolean>>(
29
- options.reduce((acc, option) => {
30
- acc[option.id] = option.defaultValue ?? false;
31
- return acc;
32
- }, {} as Record<string, boolean>)
33
- );
34
-
35
- const handleToggle = (id: string) => {
36
- const option = options.find(opt => opt.id === id);
37
- if (option?.required) return;
38
-
39
- setConsents(prev => ({
40
- ...prev,
41
- [id]: !prev[id]
42
- }));
43
- };
44
-
45
- const handleAcceptAll = () => {
46
- const allConsents = options.reduce((acc, option) => {
47
- acc[option.id] = true;
48
- return acc;
49
- }, {} as Record<string, boolean>);
50
-
51
- setConsents(allConsents);
52
- onSave(allConsents);
53
- };
54
-
55
- const handleSave = () => {
56
- onSave(consents);
57
- };
58
-
59
- const handleRejectAll = () => {
60
- const rejectedConsents = options.reduce((acc, option) => {
61
- acc[option.id] = option.required ? true : false;
62
- return acc;
63
- }, {} as Record<string, boolean>);
64
-
65
- setConsents(rejectedConsents);
66
- onSave(rejectedConsents);
67
- };
68
-
69
- return (
70
- <div className={`fixed inset-0 z-50 flex items-end sm:items-center justify-center p-4 bg-black/50 ${className}`}>
71
- <Card className="w-full max-w-3xl max-h-[90vh] overflow-auto shadow-xl animate-in fade-in slide-in-from-bottom-5 duration-300">
72
- <CardHeader className="border-b border-gray-200 dark:border-gray-700">
73
- <div className="flex items-center justify-between">
74
- <div>
75
- <CardTitle className="text-xl">{title}</CardTitle>
76
- <CardDescription className="mt-2">{description}</CardDescription>
77
- </div>
78
- <Badge variant="primary" className="hidden sm:flex">Privacy Settings</Badge>
79
- </div>
80
- <a
81
- href={privacyPolicyUrl}
82
- className="text-sm text-blue-600 dark:text-blue-400 hover:underline mt-2 inline-flex items-center"
83
- >
84
- <svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
85
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
86
- </svg>
87
- View our Privacy Policy
88
- </a>
89
- </CardHeader>
90
-
91
- <CardContent className="space-y-4 p-6">
92
- <div className="grid gap-4">
93
- {options.map((option) => (
94
- <div
95
- key={option.id}
96
- className={`p-4 rounded-lg border ${consents[option.id] ? 'border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-900/20' : 'border-gray-200 dark:border-gray-700'} transition-colors duration-200`}
97
- >
98
- <div className="flex items-start">
99
- <div className="flex-1">
100
- <div className="flex items-center">
101
- <h3 className="text-sm font-medium text-gray-900 dark:text-white">
102
- {option.label}
103
- </h3>
104
- {option.required && (
105
- <Badge variant="secondary" className="ml-2 text-xs">Required</Badge>
106
- )}
107
- </div>
108
- <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
109
- {option.description}
110
- </p>
111
- </div>
112
- <div className="ml-3 flex h-5 items-center">
113
- <Checkbox
114
- id={`consent-${option.id}`}
115
- checked={consents[option.id]}
116
- onChange={() => handleToggle(option.id)}
117
- disabled={option.required}
118
- />
119
- </div>
120
- </div>
121
- </div>
122
- ))}
123
- </div>
124
- </CardContent>
125
-
126
- <CardFooter className="flex flex-col sm:flex-row sm:justify-between border-t border-gray-200 dark:border-gray-700 p-6 bg-gray-50 dark:bg-gray-800">
127
- <div className="mb-4 sm:mb-0">
128
- <p className="text-xs text-gray-500 dark:text-gray-400">
129
- You can change your preferences at any time by visiting your account settings.
130
- </p>
131
- </div>
132
- <div className="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
133
- <Button
134
- variant="outline"
135
- onClick={handleRejectAll}
136
- className="sm:order-1"
137
- >
138
- Reject All
139
- </Button>
140
- <Button
141
- variant="secondary"
142
- onClick={handleSave}
143
- className="sm:order-2"
144
- >
145
- Save Preferences
146
- </Button>
147
- <Button
148
- variant="default"
149
- onClick={handleAcceptAll}
150
- className="sm:order-3"
151
- >
152
- Accept All
153
- </Button>
154
- </div>
155
- </CardFooter>
156
- </Card>
157
- </div>
158
- );
159
- }