@tantainnovative/ndpr-toolkit 1.0.3 → 1.0.5

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 (110) hide show
  1. package/next-env.d.ts +5 -0
  2. package/package.json +1 -1
  3. package/packages/ndpr-toolkit/dist/components/breach/BreachNotificationManager.d.ts +62 -0
  4. package/packages/ndpr-toolkit/dist/components/breach/BreachReportForm.d.ts +66 -0
  5. package/packages/ndpr-toolkit/dist/components/breach/BreachRiskAssessment.d.ts +50 -0
  6. package/packages/ndpr-toolkit/dist/components/breach/RegulatoryReportGenerator.d.ts +94 -0
  7. package/packages/ndpr-toolkit/dist/components/consent/ConsentBanner.d.ts +79 -0
  8. package/packages/ndpr-toolkit/dist/components/consent/ConsentManager.d.ts +73 -0
  9. package/packages/ndpr-toolkit/dist/components/consent/ConsentStorage.d.ts +41 -0
  10. package/packages/ndpr-toolkit/dist/components/dpia/DPIAQuestionnaire.d.ts +70 -0
  11. package/packages/ndpr-toolkit/dist/components/dpia/DPIAReport.d.ts +40 -0
  12. package/packages/ndpr-toolkit/dist/components/dpia/StepIndicator.d.ts +64 -0
  13. package/packages/ndpr-toolkit/dist/components/dsr/DSRDashboard.d.ts +58 -0
  14. package/packages/ndpr-toolkit/dist/components/dsr/DSRRequestForm.d.ts +74 -0
  15. package/packages/ndpr-toolkit/dist/components/dsr/DSRTracker.d.ts +56 -0
  16. package/packages/ndpr-toolkit/dist/components/policy/PolicyExporter.d.ts +65 -0
  17. package/packages/ndpr-toolkit/dist/components/policy/PolicyGenerator.d.ts +54 -0
  18. package/packages/ndpr-toolkit/dist/components/policy/PolicyPreview.d.ts +71 -0
  19. package/packages/ndpr-toolkit/dist/hooks/useBreach.d.ts +97 -0
  20. package/packages/ndpr-toolkit/dist/hooks/useConsent.d.ts +63 -0
  21. package/packages/ndpr-toolkit/dist/hooks/useDPIA.d.ts +92 -0
  22. package/packages/ndpr-toolkit/dist/hooks/useDSR.d.ts +72 -0
  23. package/packages/ndpr-toolkit/dist/hooks/usePrivacyPolicy.d.ts +87 -0
  24. package/packages/ndpr-toolkit/dist/index.d.ts +31 -0
  25. package/packages/ndpr-toolkit/dist/index.esm.js +2 -0
  26. package/packages/ndpr-toolkit/dist/index.esm.js.map +1 -0
  27. package/packages/ndpr-toolkit/dist/index.js +2 -0
  28. package/packages/ndpr-toolkit/dist/index.js.map +1 -0
  29. package/packages/ndpr-toolkit/dist/setupTests.d.ts +2 -0
  30. package/packages/ndpr-toolkit/dist/types/breach.d.ts +239 -0
  31. package/packages/ndpr-toolkit/dist/types/consent.d.ts +95 -0
  32. package/packages/ndpr-toolkit/dist/types/dpia.d.ts +196 -0
  33. package/packages/ndpr-toolkit/dist/types/dsr.d.ts +162 -0
  34. package/packages/ndpr-toolkit/dist/types/privacy.d.ts +204 -0
  35. package/packages/ndpr-toolkit/dist/utils/breach.d.ts +14 -0
  36. package/packages/ndpr-toolkit/dist/utils/consent.d.ts +10 -0
  37. package/packages/ndpr-toolkit/dist/utils/dpia.d.ts +12 -0
  38. package/packages/ndpr-toolkit/dist/utils/dsr.d.ts +11 -0
  39. package/packages/ndpr-toolkit/dist/utils/privacy.d.ts +12 -0
  40. package/src/components/consent/ConsentBanner.tsx +82 -48
  41. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +240 -129
  42. package/src/components/dpia/DPIAQuestionnaire.tsx +162 -122
  43. package/src/components/privacy-policy/PolicyGenerator.tsx +5 -5
  44. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +103 -77
  45. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +117 -63
  46. package/src/hooks/useConsent.ts +16 -10
  47. package/src/lib/consentService.ts +44 -37
  48. package/src/lib/dpiaQuestions.ts +139 -99
  49. package/src/lib/requestService.ts +21 -17
  50. package/src/types/index.ts +13 -8
  51. package/.claude/settings.local.json +0 -20
  52. package/.eslintrc.json +0 -10
  53. package/.github/workflows/ci.yml +0 -36
  54. package/.github/workflows/nextjs.yml +0 -104
  55. package/.husky/commit-msg +0 -4
  56. package/.husky/pre-commit +0 -4
  57. package/.lintstagedrc.js +0 -4
  58. package/.nvmrc +0 -1
  59. package/.versionrc +0 -17
  60. package/CLAUDE.md +0 -90
  61. package/commitlint.config.js +0 -36
  62. package/jest.config.js +0 -31
  63. package/jest.setup.js +0 -15
  64. package/packages/ndpr-toolkit/jest.config.js +0 -23
  65. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +0 -119
  66. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +0 -122
  67. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +0 -270
  68. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +0 -199
  69. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +0 -224
  70. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +0 -104
  71. package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +0 -161
  72. package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +0 -330
  73. package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +0 -149
  74. package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +0 -88
  75. package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +0 -160
  76. package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +0 -110
  77. package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +0 -97
  78. package/src/__tests__/example.test.ts +0 -13
  79. package/src/__tests__/requestService.test.ts +0 -57
  80. package/src/app/docs/components/DocLayout.tsx +0 -267
  81. package/src/app/docs/components/breach-notification/page.tsx +0 -797
  82. package/src/app/docs/components/consent-management/page.tsx +0 -576
  83. package/src/app/docs/components/data-subject-rights/page.tsx +0 -511
  84. package/src/app/docs/components/dpia-questionnaire/layout.tsx +0 -15
  85. package/src/app/docs/components/dpia-questionnaire/metadata.ts +0 -31
  86. package/src/app/docs/components/dpia-questionnaire/page.tsx +0 -666
  87. package/src/app/docs/components/hooks/page.tsx +0 -305
  88. package/src/app/docs/components/page.tsx +0 -84
  89. package/src/app/docs/components/privacy-policy-generator/page.tsx +0 -634
  90. package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +0 -123
  91. package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +0 -328
  92. package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +0 -28
  93. package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +0 -91
  94. package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +0 -118
  95. package/src/app/docs/guides/breach-notification-process/page.tsx +0 -39
  96. package/src/app/docs/guides/conducting-dpia/page.tsx +0 -593
  97. package/src/app/docs/guides/data-subject-requests/page.tsx +0 -666
  98. package/src/app/docs/guides/managing-consent/page.tsx +0 -738
  99. package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +0 -296
  100. package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +0 -145
  101. package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +0 -33
  102. package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +0 -99
  103. package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +0 -159
  104. package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +0 -38
  105. package/src/app/docs/guides/page.tsx +0 -67
  106. package/src/app/docs/layout.tsx +0 -15
  107. package/src/app/docs/metadata.ts +0 -31
  108. package/src/app/docs/page.tsx +0 -572
  109. package/src/components/docs/DocLayout.tsx +0 -289
  110. package/src/components/docs/index.ts +0 -2
@@ -1,13 +1,13 @@
1
- 'use client';
1
+ "use client";
2
2
 
3
- import { useState, useEffect } from 'react';
4
- import { RiskAssessmentQuestion } from '@/types';
5
- import { Button } from '@/components/ui/Button';
6
- import { Input } from '@/components/ui/Input';
7
- import { TextArea } from '@/components/ui/TextArea';
8
- import { FormField } from '@/components/ui/FormField';
9
- import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
10
- import { Badge } from '@/components/ui/Badge';
3
+ import { useState, useEffect } from "react";
4
+ import { RiskAssessmentQuestion } from "@/types";
5
+ import { Button } from "@/components/ui/Button";
6
+ import { Input } from "@/components/ui/Input";
7
+ import { TextArea } from "@/components/ui/TextArea";
8
+ import { FormField } from "@/components/ui/FormField";
9
+ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/Card";
10
+ import { Badge } from "@/components/ui/Badge";
11
11
 
12
12
  interface DPIAQuestionnaireProps {
13
13
  questions: RiskAssessmentQuestion[];
@@ -18,12 +18,14 @@ interface DPIAQuestionnaireProps {
18
18
  export default function DPIAQuestionnaire({
19
19
  questions,
20
20
  onSubmit,
21
- className = '',
21
+ className = "",
22
22
  }: DPIAQuestionnaireProps) {
23
- const [projectName, setProjectName] = useState('');
24
- const [projectDescription, setProjectDescription] = useState('');
25
- const [dataController, setDataController] = useState('');
26
- const [assessmentDate, setAssessmentDate] = useState(new Date().toISOString().split('T')[0]);
23
+ const [projectName, setProjectName] = useState("");
24
+ const [projectDescription, setProjectDescription] = useState("");
25
+ const [dataController, setDataController] = useState("");
26
+ const [assessmentDate, setAssessmentDate] = useState(
27
+ new Date().toISOString().split("T")[0],
28
+ );
27
29
  const [answers, setAnswers] = useState<Record<string, number>>({});
28
30
  const [currentStep, setCurrentStep] = useState(0);
29
31
  const [errors, setErrors] = useState<Record<string, string>>({});
@@ -31,7 +33,7 @@ export default function DPIAQuestionnaire({
31
33
 
32
34
  // Since DPIAQuestion doesn't have a category, we'll create a single category
33
35
  const categories: Record<string, RiskAssessmentQuestion[]> = {
34
- 'Assessment Questions': questions
36
+ "Assessment Questions": questions,
35
37
  };
36
38
 
37
39
  const categoryNames = Object.keys(categories);
@@ -39,9 +41,9 @@ export default function DPIAQuestionnaire({
39
41
  const handleAnswerChange = (questionId: string, value: number | string) => {
40
42
  setAnswers((prev) => ({
41
43
  ...prev,
42
- [questionId]: typeof value === 'string' ? parseInt(value, 10) : value,
44
+ [questionId]: typeof value === "string" ? parseInt(value, 10) : value,
43
45
  }));
44
-
46
+
45
47
  // Clear error when question is answered
46
48
  if (errors[questionId]) {
47
49
  setErrors((prev) => {
@@ -55,34 +57,34 @@ export default function DPIAQuestionnaire({
55
57
  // Render different input types based on question type
56
58
  const renderQuestionInput = (question: RiskAssessmentQuestion) => {
57
59
  switch (question.type) {
58
- case 'text':
60
+ case "text":
59
61
  return (
60
62
  <Input
61
63
  id={question.id}
62
- value={answers[question.id] || ''}
64
+ value={answers[question.id] || ""}
63
65
  onChange={(e) => handleAnswerChange(question.id, e.target.value)}
64
66
  placeholder={question.guidance}
65
67
  className="w-full"
66
68
  />
67
69
  );
68
-
69
- case 'textarea':
70
+
71
+ case "textarea":
70
72
  return (
71
73
  <TextArea
72
74
  id={question.id}
73
- value={answers[question.id] || ''}
75
+ value={answers[question.id] || ""}
74
76
  onChange={(e) => handleAnswerChange(question.id, e.target.value)}
75
77
  placeholder={question.guidance}
76
78
  rows={4}
77
79
  className="w-full"
78
80
  />
79
81
  );
80
-
81
- case 'select':
82
+
83
+ case "select":
82
84
  return (
83
85
  <select
84
86
  id={question.id}
85
- value={answers[question.id] || ''}
87
+ value={answers[question.id] || ""}
86
88
  onChange={(e) => handleAnswerChange(question.id, e.target.value)}
87
89
  className="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
88
90
  >
@@ -94,14 +96,16 @@ export default function DPIAQuestionnaire({
94
96
  ))}
95
97
  </select>
96
98
  );
97
-
98
- case 'radio':
99
+
100
+ case "radio":
99
101
  return question.options?.map((option) => (
100
- <div
101
- key={option.value}
102
- className={`p-3 border rounded-md cursor-pointer transition-colors ${answers[question.id] === parseInt(option.value)
103
- ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30 dark:border-blue-400'
104
- : 'border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800'}`}
102
+ <div
103
+ key={option.value}
104
+ className={`p-3 border rounded-md cursor-pointer transition-colors ${
105
+ answers[question.id] === parseInt(option.value)
106
+ ? "border-blue-500 bg-blue-50 dark:bg-blue-900/30 dark:border-blue-400"
107
+ : "border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800"
108
+ }`}
105
109
  onClick={() => handleAnswerChange(question.id, option.value)}
106
110
  >
107
111
  <div className="flex items-center justify-between">
@@ -122,12 +126,13 @@ export default function DPIAQuestionnaire({
122
126
  {option.label}
123
127
  </label>
124
128
  </div>
125
- {option.riskLevel && getRiskLevelIndicator(parseInt(option.value))}
129
+ {option.riskLevel &&
130
+ getRiskLevelIndicator(parseInt(option.value))}
126
131
  </div>
127
132
  </div>
128
133
  ));
129
-
130
- case 'checkbox':
134
+
135
+ case "checkbox":
131
136
  return question.options?.map((option) => (
132
137
  <div key={option.value} className="flex items-center">
133
138
  <input
@@ -136,14 +141,16 @@ export default function DPIAQuestionnaire({
136
141
  value={option.value}
137
142
  checked={answers[question.id]?.toString().includes(option.value)}
138
143
  onChange={(e) => {
139
- const currentValues = answers[question.id]?.toString().split(',').filter(Boolean) || [];
144
+ const currentValues =
145
+ answers[question.id]?.toString().split(",").filter(Boolean) ||
146
+ [];
140
147
  if (e.target.checked) {
141
148
  currentValues.push(option.value);
142
149
  } else {
143
150
  const index = currentValues.indexOf(option.value);
144
151
  if (index > -1) currentValues.splice(index, 1);
145
152
  }
146
- handleAnswerChange(question.id, currentValues.join(','));
153
+ handleAnswerChange(question.id, currentValues.join(","));
147
154
  }}
148
155
  className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500 rounded"
149
156
  />
@@ -155,8 +162,8 @@ export default function DPIAQuestionnaire({
155
162
  </label>
156
163
  </div>
157
164
  ));
158
-
159
- case 'scale':
165
+
166
+ case "scale":
160
167
  return (
161
168
  <div className="space-y-2">
162
169
  <input
@@ -182,9 +189,13 @@ export default function DPIAQuestionnaire({
182
189
  </div>
183
190
  </div>
184
191
  );
185
-
192
+
186
193
  default:
187
- return <p className="text-gray-500">Unsupported question type: {question.type}</p>;
194
+ return (
195
+ <p className="text-gray-500">
196
+ Unsupported question type: {question.type}
197
+ </p>
198
+ );
188
199
  }
189
200
  };
190
201
 
@@ -192,32 +203,34 @@ export default function DPIAQuestionnaire({
192
203
  useEffect(() => {
193
204
  const totalQuestions = questions.length;
194
205
  const answeredQuestions = Object.keys(answers).length;
195
- const projectInfoComplete = projectName.trim() !== '' ? 1 : 0;
196
-
206
+ const projectInfoComplete = projectName.trim() !== "" ? 1 : 0;
207
+
197
208
  // Calculate progress percentage (project info counts as one question)
198
- const calculatedProgress = Math.round(((answeredQuestions + projectInfoComplete) / (totalQuestions + 1)) * 100);
209
+ const calculatedProgress = Math.round(
210
+ ((answeredQuestions + projectInfoComplete) / (totalQuestions + 1)) * 100,
211
+ );
199
212
  setProgress(calculatedProgress);
200
213
  }, [answers, projectName, questions.length]);
201
214
 
202
215
  const validateStep = (step: number) => {
203
216
  const newErrors: Record<string, string> = {};
204
-
217
+
205
218
  if (step === 0) {
206
219
  if (!projectName.trim()) {
207
- newErrors.projectName = 'Project name is required';
220
+ newErrors.projectName = "Project name is required";
208
221
  }
209
222
  // Description and data controller are optional, so no validation needed
210
223
  } else {
211
224
  const category = categoryNames[step - 1];
212
225
  const categoryQuestions = categories[category];
213
-
226
+
214
227
  categoryQuestions.forEach((question) => {
215
228
  if (answers[question.id] === undefined) {
216
- newErrors[question.id] = 'Please select an answer for this question';
229
+ newErrors[question.id] = "Please select an answer for this question";
217
230
  }
218
231
  });
219
232
  }
220
-
233
+
221
234
  setErrors(newErrors);
222
235
  return Object.keys(newErrors).length === 0;
223
236
  };
@@ -243,15 +256,19 @@ export default function DPIAQuestionnaire({
243
256
  if (currentStep === 0) {
244
257
  return (
245
258
  <div className="space-y-6">
246
- <h3 className="text-lg font-medium text-gray-900 dark:text-white">Project Information</h3>
247
-
259
+ <h3 className="text-lg font-medium text-gray-900 dark:text-white">
260
+ Project Information
261
+ </h3>
262
+
248
263
  <div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-md mb-6">
249
264
  <p className="text-sm text-blue-800 dark:text-blue-200">
250
- This Data Protection Impact Assessment (DPIA) will help you identify and minimize data protection risks in your project.
251
- Please provide accurate information to receive the most relevant recommendations.
265
+ This Data Protection Impact Assessment (DPIA) will help you
266
+ identify and minimize data protection risks in your project.
267
+ Please provide accurate information to receive the most relevant
268
+ recommendations.
252
269
  </p>
253
270
  </div>
254
-
271
+
255
272
  <FormField
256
273
  id="projectName"
257
274
  label="Project Name"
@@ -265,7 +282,7 @@ export default function DPIAQuestionnaire({
265
282
  placeholder="Enter the name of your project or data processing activity"
266
283
  />
267
284
  </FormField>
268
-
285
+
269
286
  <FormField
270
287
  id="projectDescription"
271
288
  label="Project Description"
@@ -279,7 +296,7 @@ export default function DPIAQuestionnaire({
279
296
  placeholder="Briefly describe the purpose and scope of your project"
280
297
  />
281
298
  </FormField>
282
-
299
+
283
300
  <FormField
284
301
  id="dataController"
285
302
  label="Data Controller"
@@ -292,11 +309,8 @@ export default function DPIAQuestionnaire({
292
309
  placeholder="Name of the organization responsible for the data"
293
310
  />
294
311
  </FormField>
295
-
296
- <FormField
297
- id="assessmentDate"
298
- label="Assessment Date"
299
- >
312
+
313
+ <FormField id="assessmentDate" label="Assessment Date">
300
314
  <Input
301
315
  type="date"
302
316
  id="assessmentDate"
@@ -315,34 +329,45 @@ export default function DPIAQuestionnaire({
315
329
  <div className="space-y-6">
316
330
  <div className="flex items-center space-x-2">
317
331
  <div className="w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center">
318
- <span className="text-blue-600 dark:text-blue-300 font-medium">{currentStep}</span>
332
+ <span className="text-blue-600 dark:text-blue-300 font-medium">
333
+ {currentStep}
334
+ </span>
319
335
  </div>
320
- <h3 className="text-lg font-medium text-gray-900 dark:text-white">{category}</h3>
336
+ <h3 className="text-lg font-medium text-gray-900 dark:text-white">
337
+ {category}
338
+ </h3>
321
339
  </div>
322
-
340
+
323
341
  <div className="bg-gray-50 dark:bg-gray-800 p-4 rounded-md mb-4">
324
342
  <p className="text-sm text-gray-600 dark:text-gray-400">
325
343
  {getCategoryDescription(category)}
326
344
  </p>
327
345
  </div>
328
-
346
+
329
347
  {categoryQuestions.map((question, index) => (
330
- <div key={question.id} className="p-4 border border-gray-200 dark:border-gray-700 rounded-lg space-y-4">
348
+ <div
349
+ key={question.id}
350
+ className="p-4 border border-gray-200 dark:border-gray-700 rounded-lg space-y-4"
351
+ >
331
352
  <div className="flex items-start">
332
353
  <div className="flex-shrink-0 w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center mr-3 mt-0.5">
333
- <span className="text-blue-600 dark:text-blue-300 text-xs font-medium">{index + 1}</span>
354
+ <span className="text-blue-600 dark:text-blue-300 text-xs font-medium">
355
+ {index + 1}
356
+ </span>
334
357
  </div>
335
358
  <label className="block text-sm font-medium text-gray-800 dark:text-gray-200">
336
359
  {question.text}
337
360
  </label>
338
361
  </div>
339
-
362
+
340
363
  <div className="ml-9 space-y-2">
341
364
  {renderQuestionInput(question)}
342
365
  </div>
343
-
366
+
344
367
  {errors[question.id] && (
345
- <p className="ml-9 text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
368
+ <p className="ml-9 text-sm text-red-600 dark:text-red-400">
369
+ {errors[question.id]}
370
+ </p>
346
371
  )}
347
372
  </div>
348
373
  ))}
@@ -353,37 +378,40 @@ export default function DPIAQuestionnaire({
353
378
  // Helper function to get category descriptions
354
379
  const getCategoryDescription = (category: string): string => {
355
380
  switch (category) {
356
- case 'Data Collection':
357
- return 'These questions help assess the risks associated with the types of personal data you collect and the sources of that data.';
358
- case 'Data Processing':
359
- return 'These questions evaluate how you use, analyze, and transform the personal data within your project.';
360
- case 'Data Sharing':
361
- return 'These questions examine the risks of sharing personal data with third parties or transferring it across borders.';
362
- case 'Security Measures':
363
- return 'These questions assess the safeguards you have in place to protect personal data from unauthorized access or breaches.';
364
- case 'Data Subject Rights':
365
- return 'These questions evaluate how well your project supports individuals in exercising their rights over their personal data.';
381
+ case "Data Collection":
382
+ return "These questions help assess the risks associated with the types of personal data you collect and the sources of that data.";
383
+ case "Data Processing":
384
+ return "These questions evaluate how you use, analyze, and transform the personal data within your project.";
385
+ case "Data Sharing":
386
+ return "These questions examine the risks of sharing personal data with third parties or transferring it across borders.";
387
+ case "Security Measures":
388
+ return "These questions assess the safeguards you have in place to protect personal data from unauthorized access or breaches.";
389
+ case "Data Subject Rights":
390
+ return "These questions evaluate how well your project supports individuals in exercising their rights over their personal data.";
366
391
  default:
367
- return 'Please answer the following questions about your project.';
392
+ return "Please answer the following questions about your project.";
368
393
  }
369
394
  };
370
395
 
371
396
  // Helper function to display risk level indicators
372
397
  const getRiskLevelIndicator = (value: number) => {
373
- const variants: Record<number, 'success' | 'warning' | 'danger' | 'primary'> = {
374
- 1: 'success',
375
- 2: 'warning',
376
- 3: 'danger',
377
- 4: 'danger'
398
+ const variants: Record<
399
+ number,
400
+ "success" | "warning" | "danger" | "primary"
401
+ > = {
402
+ 1: "success",
403
+ 2: "warning",
404
+ 3: "danger",
405
+ 4: "danger",
378
406
  };
379
-
407
+
380
408
  const labels = {
381
- 1: 'Low Risk',
382
- 2: 'Medium Risk',
383
- 3: 'High Risk',
384
- 4: 'Very High Risk'
409
+ 1: "Low Risk",
410
+ 2: "Medium Risk",
411
+ 3: "High Risk",
412
+ 4: "Very High Risk",
385
413
  };
386
-
414
+
387
415
  return (
388
416
  <div className="flex items-center ml-auto pl-2">
389
417
  <Badge variant={variants[value as keyof typeof variants]}>
@@ -403,7 +431,7 @@ export default function DPIAQuestionnaire({
403
431
  Assess the data protection risks of your project with our DPIA tool.
404
432
  </p>
405
433
  </CardHeader>
406
-
434
+
407
435
  <CardContent>
408
436
  <div className="mb-6">
409
437
  <div className="flex items-center justify-between mb-2">
@@ -412,7 +440,7 @@ export default function DPIAQuestionnaire({
412
440
  </div>
413
441
  <div className="text-sm font-medium text-gray-900 dark:text-white">
414
442
  {currentStep === 0
415
- ? 'Project Information'
443
+ ? "Project Information"
416
444
  : categoryNames[currentStep - 1]}
417
445
  </div>
418
446
  </div>
@@ -428,51 +456,63 @@ export default function DPIAQuestionnaire({
428
456
  </div>
429
457
  {currentStep > 0 && currentStep < totalSteps && (
430
458
  <div className="text-xs text-gray-500 dark:text-gray-400">
431
- {categories[categoryNames[currentStep - 1]].filter(q =>
432
- answers[q.id] !== undefined
433
- ).length} of {categories[categoryNames[currentStep - 1]].length} questions answered
459
+ {
460
+ categories[categoryNames[currentStep - 1]].filter(
461
+ (q) => answers[q.id] !== undefined,
462
+ ).length
463
+ }{" "}
464
+ of {categories[categoryNames[currentStep - 1]].length} questions
465
+ answered
434
466
  </div>
435
467
  )}
436
468
  </div>
437
469
  </div>
438
-
439
- <div className="mb-6">
440
- {renderStepContent()}
441
- </div>
442
-
470
+
471
+ <div className="mb-6">{renderStepContent()}</div>
472
+
443
473
  <div className="flex justify-between pt-4 border-t border-gray-200 dark:border-gray-700">
444
474
  {currentStep > 0 ? (
445
- <Button
446
- type="button"
447
- onClick={handleBack}
448
- variant="outline"
449
- >
450
- <svg className="w-5 h-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
451
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
475
+ <Button type="button" onClick={handleBack} variant="outline">
476
+ <svg
477
+ className="w-5 h-5 mr-1"
478
+ fill="none"
479
+ stroke="currentColor"
480
+ viewBox="0 0 24 24"
481
+ xmlns="http://www.w3.org/2000/svg"
482
+ >
483
+ <path
484
+ strokeLinecap="round"
485
+ strokeLinejoin="round"
486
+ strokeWidth={2}
487
+ d="M15 19l-7-7 7-7"
488
+ />
452
489
  </svg>
453
490
  Back
454
491
  </Button>
455
492
  ) : (
456
493
  <div>{/* Empty div to maintain layout */}</div>
457
494
  )}
458
-
495
+
459
496
  {currentStep < totalSteps - 1 ? (
460
- <Button
461
- type="button"
462
- onClick={handleNext}
463
- variant="default"
464
- >
497
+ <Button type="button" onClick={handleNext} variant="default">
465
498
  Next
466
- <svg className="w-5 h-5 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
467
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
499
+ <svg
500
+ className="w-5 h-5 ml-1"
501
+ fill="none"
502
+ stroke="currentColor"
503
+ viewBox="0 0 24 24"
504
+ xmlns="http://www.w3.org/2000/svg"
505
+ >
506
+ <path
507
+ strokeLinecap="round"
508
+ strokeLinejoin="round"
509
+ strokeWidth={2}
510
+ d="M9 5l7 7-7 7"
511
+ />
468
512
  </svg>
469
513
  </Button>
470
514
  ) : (
471
- <Button
472
- type="button"
473
- onClick={handleSubmit}
474
- variant="default"
475
- >
515
+ <Button type="button" onClick={handleSubmit} variant="default">
476
516
  Submit Assessment
477
517
  </Button>
478
518
  )}
@@ -634,7 +634,7 @@ export default function PolicyGenerator({
634
634
  title: "Purposes of Data Collection and Processing",
635
635
  template: `We collect and process your personal data for the following specific purposes:\n\n${formData.dataCollectionPurposes.map((purpose) => `- ${purpose}`).join("\n")}${formData.includeExamples ? `\n\n${getIndustrySpecificPurposeExample(formData.industryCategory)}` : ""}`,
636
636
  required: true,
637
- included: true,
637
+ included: true,
638
638
  order: order++,
639
639
  });
640
640
 
@@ -666,7 +666,7 @@ export default function PolicyGenerator({
666
666
  title: "Data Retention",
667
667
  template: `We will retain your personal data for ${formData.dataRetentionPeriod}, or for as long as necessary to fulfill the purposes for which it was collected, including for the purposes of satisfying any legal, accounting, or reporting requirements. ${formData.includeNDPRCompliance ? `This is in accordance with ${formData.includeLegalReferences ? "Section 2.1(1)(e) of the NDPR and Section 33 of the DPA" : "the data retention principles of the NDPR and DPA"}.` : ""}\n\nTo determine the appropriate retention period for personal data, we consider the amount, nature, and sensitivity of the personal data, the potential risk of harm from unauthorized use or disclosure of your personal data, the purposes for which we process your personal data and whether we can achieve those purposes through other means, and the applicable legal requirements.`,
668
668
  required: true,
669
- included: true,
669
+ included: true,
670
670
  order: order++,
671
671
  });
672
672
 
@@ -797,7 +797,7 @@ export default function PolicyGenerator({
797
797
  title: "Your Rights",
798
798
  template: rightsContent,
799
799
  required: true,
800
- included: true,
800
+ included: true,
801
801
  order: order++,
802
802
  });
803
803
 
@@ -807,7 +807,7 @@ export default function PolicyGenerator({
807
807
  title: "Changes to This Privacy Policy",
808
808
  template: `We may update this Privacy Policy from time to time. The current version of the Privacy Policy is effective as of ${formData.policyEffectiveDate} (Version ${formData.policyVersion}).${formData.previousPolicyUrl ? ` Previous versions of this Policy can be found at ${formData.previousPolicyUrl}.` : ""}\n\n${formData.policyUpdateProcedure ? `When we make changes to this Privacy Policy: ${formData.policyUpdateProcedure}` : "We will notify you of any material changes to this Privacy Policy by posting the updated Policy on our website and, where appropriate, by sending you a notification."}`,
809
809
  required: true,
810
- included: true,
810
+ included: true,
811
811
  order: order++,
812
812
  });
813
813
 
@@ -865,7 +865,7 @@ export default function PolicyGenerator({
865
865
  title: "Contact Us",
866
866
  template: `If you have any questions about this Privacy Policy or our privacy practices, please contact us at:\n\n${formData.organizationName}\n${formData.organizationAddress ? `${formData.organizationAddress}\n` : ""}Email: ${formData.organizationContact}\n${formData.organizationWebsite ? `Website: ${formData.organizationWebsite}` : ""}`,
867
867
  required: true,
868
- included: true,
868
+ included: true,
869
869
  order: order++,
870
870
  });
871
871