@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.
- package/next-env.d.ts +5 -0
- package/package.json +1 -1
- package/packages/ndpr-toolkit/dist/components/breach/BreachNotificationManager.d.ts +62 -0
- package/packages/ndpr-toolkit/dist/components/breach/BreachReportForm.d.ts +66 -0
- package/packages/ndpr-toolkit/dist/components/breach/BreachRiskAssessment.d.ts +50 -0
- package/packages/ndpr-toolkit/dist/components/breach/RegulatoryReportGenerator.d.ts +94 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentBanner.d.ts +79 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentManager.d.ts +73 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentStorage.d.ts +41 -0
- package/packages/ndpr-toolkit/dist/components/dpia/DPIAQuestionnaire.d.ts +70 -0
- package/packages/ndpr-toolkit/dist/components/dpia/DPIAReport.d.ts +40 -0
- package/packages/ndpr-toolkit/dist/components/dpia/StepIndicator.d.ts +64 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRDashboard.d.ts +58 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRRequestForm.d.ts +74 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRTracker.d.ts +56 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyExporter.d.ts +65 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyGenerator.d.ts +54 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyPreview.d.ts +71 -0
- package/packages/ndpr-toolkit/dist/hooks/useBreach.d.ts +97 -0
- package/packages/ndpr-toolkit/dist/hooks/useConsent.d.ts +63 -0
- package/packages/ndpr-toolkit/dist/hooks/useDPIA.d.ts +92 -0
- package/packages/ndpr-toolkit/dist/hooks/useDSR.d.ts +72 -0
- package/packages/ndpr-toolkit/dist/hooks/usePrivacyPolicy.d.ts +87 -0
- package/packages/ndpr-toolkit/dist/index.d.ts +31 -0
- package/packages/ndpr-toolkit/dist/index.esm.js +2 -0
- package/packages/ndpr-toolkit/dist/index.esm.js.map +1 -0
- package/packages/ndpr-toolkit/dist/index.js +2 -0
- package/packages/ndpr-toolkit/dist/index.js.map +1 -0
- package/packages/ndpr-toolkit/dist/setupTests.d.ts +2 -0
- package/packages/ndpr-toolkit/dist/types/breach.d.ts +239 -0
- package/packages/ndpr-toolkit/dist/types/consent.d.ts +95 -0
- package/packages/ndpr-toolkit/dist/types/dpia.d.ts +196 -0
- package/packages/ndpr-toolkit/dist/types/dsr.d.ts +162 -0
- package/packages/ndpr-toolkit/dist/types/privacy.d.ts +204 -0
- package/packages/ndpr-toolkit/dist/utils/breach.d.ts +14 -0
- package/packages/ndpr-toolkit/dist/utils/consent.d.ts +10 -0
- package/packages/ndpr-toolkit/dist/utils/dpia.d.ts +12 -0
- package/packages/ndpr-toolkit/dist/utils/dsr.d.ts +11 -0
- package/packages/ndpr-toolkit/dist/utils/privacy.d.ts +12 -0
- package/src/components/consent/ConsentBanner.tsx +82 -48
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +240 -129
- package/src/components/dpia/DPIAQuestionnaire.tsx +162 -122
- package/src/components/privacy-policy/PolicyGenerator.tsx +5 -5
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +103 -77
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +117 -63
- package/src/hooks/useConsent.ts +16 -10
- package/src/lib/consentService.ts +44 -37
- package/src/lib/dpiaQuestions.ts +139 -99
- package/src/lib/requestService.ts +21 -17
- package/src/types/index.ts +13 -8
- package/.claude/settings.local.json +0 -20
- package/.eslintrc.json +0 -10
- package/.github/workflows/ci.yml +0 -36
- package/.github/workflows/nextjs.yml +0 -104
- package/.husky/commit-msg +0 -4
- package/.husky/pre-commit +0 -4
- package/.lintstagedrc.js +0 -4
- package/.nvmrc +0 -1
- package/.versionrc +0 -17
- package/CLAUDE.md +0 -90
- package/commitlint.config.js +0 -36
- package/jest.config.js +0 -31
- package/jest.setup.js +0 -15
- package/packages/ndpr-toolkit/jest.config.js +0 -23
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +0 -119
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +0 -122
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +0 -270
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +0 -199
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +0 -224
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +0 -104
- package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +0 -161
- package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +0 -330
- package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +0 -149
- package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +0 -88
- package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +0 -160
- package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +0 -110
- package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +0 -97
- package/src/__tests__/example.test.ts +0 -13
- package/src/__tests__/requestService.test.ts +0 -57
- package/src/app/docs/components/DocLayout.tsx +0 -267
- package/src/app/docs/components/breach-notification/page.tsx +0 -797
- package/src/app/docs/components/consent-management/page.tsx +0 -576
- package/src/app/docs/components/data-subject-rights/page.tsx +0 -511
- package/src/app/docs/components/dpia-questionnaire/layout.tsx +0 -15
- package/src/app/docs/components/dpia-questionnaire/metadata.ts +0 -31
- package/src/app/docs/components/dpia-questionnaire/page.tsx +0 -666
- package/src/app/docs/components/hooks/page.tsx +0 -305
- package/src/app/docs/components/page.tsx +0 -84
- package/src/app/docs/components/privacy-policy-generator/page.tsx +0 -634
- package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +0 -123
- package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +0 -328
- package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +0 -28
- package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +0 -91
- package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +0 -118
- package/src/app/docs/guides/breach-notification-process/page.tsx +0 -39
- package/src/app/docs/guides/conducting-dpia/page.tsx +0 -593
- package/src/app/docs/guides/data-subject-requests/page.tsx +0 -666
- package/src/app/docs/guides/managing-consent/page.tsx +0 -738
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +0 -296
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +0 -145
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +0 -33
- package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +0 -99
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +0 -159
- package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +0 -38
- package/src/app/docs/guides/page.tsx +0 -67
- package/src/app/docs/layout.tsx +0 -15
- package/src/app/docs/metadata.ts +0 -31
- package/src/app/docs/page.tsx +0 -572
- package/src/components/docs/DocLayout.tsx +0 -289
- package/src/components/docs/index.ts +0 -2
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect } from
|
|
4
|
-
import { RiskAssessmentQuestion } from
|
|
5
|
-
import { Button } from
|
|
6
|
-
import { Input } from
|
|
7
|
-
import { TextArea } from
|
|
8
|
-
import { FormField } from
|
|
9
|
-
import { Card, CardHeader, CardTitle, CardContent } from
|
|
10
|
-
import { Badge } from
|
|
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(
|
|
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
|
-
|
|
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 ===
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 ${
|
|
103
|
-
|
|
104
|
-
|
|
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 &&
|
|
129
|
+
{option.riskLevel &&
|
|
130
|
+
getRiskLevelIndicator(parseInt(option.value))}
|
|
126
131
|
</div>
|
|
127
132
|
</div>
|
|
128
133
|
));
|
|
129
|
-
|
|
130
|
-
case
|
|
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 =
|
|
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
|
|
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
|
|
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() !==
|
|
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(
|
|
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 =
|
|
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] =
|
|
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">
|
|
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
|
|
251
|
-
|
|
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">
|
|
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">
|
|
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
|
|
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">
|
|
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">
|
|
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
|
|
357
|
-
return
|
|
358
|
-
case
|
|
359
|
-
return
|
|
360
|
-
case
|
|
361
|
-
return
|
|
362
|
-
case
|
|
363
|
-
return
|
|
364
|
-
case
|
|
365
|
-
return
|
|
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
|
|
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<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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:
|
|
382
|
-
2:
|
|
383
|
-
3:
|
|
384
|
-
4:
|
|
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
|
-
?
|
|
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
|
-
{
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
|
467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
868
|
+
included: true,
|
|
869
869
|
order: order++,
|
|
870
870
|
});
|
|
871
871
|
|