@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,483 +0,0 @@
1
- 'use client';
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';
11
-
12
- interface DPIAQuestionnaireProps {
13
- questions: RiskAssessmentQuestion[];
14
- onSubmit: (answers: Record<string, number>, projectName: string) => void;
15
- className?: string;
16
- }
17
-
18
- export default function DPIAQuestionnaire({
19
- questions,
20
- onSubmit,
21
- className = '',
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]);
27
- const [answers, setAnswers] = useState<Record<string, number>>({});
28
- const [currentStep, setCurrentStep] = useState(0);
29
- const [errors, setErrors] = useState<Record<string, string>>({});
30
- const [progress, setProgress] = useState(0);
31
-
32
- // Since DPIAQuestion doesn't have a category, we'll create a single category
33
- const categories: Record<string, RiskAssessmentQuestion[]> = {
34
- 'Assessment Questions': questions
35
- };
36
-
37
- const categoryNames = Object.keys(categories);
38
-
39
- const handleAnswerChange = (questionId: string, value: number | string) => {
40
- setAnswers((prev) => ({
41
- ...prev,
42
- [questionId]: typeof value === 'string' ? parseInt(value, 10) : value,
43
- }));
44
-
45
- // Clear error when question is answered
46
- if (errors[questionId]) {
47
- setErrors((prev) => {
48
- const newErrors = { ...prev };
49
- delete newErrors[questionId];
50
- return newErrors;
51
- });
52
- }
53
- };
54
-
55
- // Render different input types based on question type
56
- const renderQuestionInput = (question: RiskAssessmentQuestion) => {
57
- switch (question.type) {
58
- case 'text':
59
- return (
60
- <Input
61
- id={question.id}
62
- value={answers[question.id] || ''}
63
- onChange={(e) => handleAnswerChange(question.id, e.target.value)}
64
- placeholder={question.guidance}
65
- className="w-full"
66
- />
67
- );
68
-
69
- case 'textarea':
70
- return (
71
- <TextArea
72
- id={question.id}
73
- value={answers[question.id] || ''}
74
- onChange={(e) => handleAnswerChange(question.id, e.target.value)}
75
- placeholder={question.guidance}
76
- rows={4}
77
- className="w-full"
78
- />
79
- );
80
-
81
- case 'select':
82
- return (
83
- <select
84
- id={question.id}
85
- value={answers[question.id] || ''}
86
- onChange={(e) => handleAnswerChange(question.id, e.target.value)}
87
- className="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
88
- >
89
- <option value="">Select an option</option>
90
- {question.options?.map((option) => (
91
- <option key={option.value} value={option.value}>
92
- {option.label}
93
- </option>
94
- ))}
95
- </select>
96
- );
97
-
98
- case 'radio':
99
- 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'}`}
105
- onClick={() => handleAnswerChange(question.id, option.value)}
106
- >
107
- <div className="flex items-center justify-between">
108
- <div className="flex items-center">
109
- <input
110
- id={`${question.id}-${option.value}`}
111
- name={question.id}
112
- type="radio"
113
- value={option.value}
114
- checked={answers[question.id] === parseInt(option.value)}
115
- onChange={() => handleAnswerChange(question.id, option.value)}
116
- className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
117
- />
118
- <label
119
- htmlFor={`${question.id}-${option.value}`}
120
- className="ml-3 block text-sm text-gray-700 dark:text-gray-300 cursor-pointer"
121
- >
122
- {option.label}
123
- </label>
124
- </div>
125
- {option.riskLevel && getRiskLevelIndicator(parseInt(option.value))}
126
- </div>
127
- </div>
128
- ));
129
-
130
- case 'checkbox':
131
- return question.options?.map((option) => (
132
- <div key={option.value} className="flex items-center">
133
- <input
134
- id={`${question.id}-${option.value}`}
135
- type="checkbox"
136
- value={option.value}
137
- checked={answers[question.id]?.toString().includes(option.value)}
138
- onChange={(e) => {
139
- const currentValues = answers[question.id]?.toString().split(',').filter(Boolean) || [];
140
- if (e.target.checked) {
141
- currentValues.push(option.value);
142
- } else {
143
- const index = currentValues.indexOf(option.value);
144
- if (index > -1) currentValues.splice(index, 1);
145
- }
146
- handleAnswerChange(question.id, currentValues.join(','));
147
- }}
148
- className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500 rounded"
149
- />
150
- <label
151
- htmlFor={`${question.id}-${option.value}`}
152
- className="ml-3 block text-sm text-gray-700 dark:text-gray-300"
153
- >
154
- {option.label}
155
- </label>
156
- </div>
157
- ));
158
-
159
- case 'scale':
160
- return (
161
- <div className="space-y-2">
162
- <input
163
- type="range"
164
- id={question.id}
165
- min={question.minValue || 1}
166
- max={question.maxValue || 5}
167
- value={answers[question.id] || question.minValue || 1}
168
- onChange={(e) => handleAnswerChange(question.id, e.target.value)}
169
- className="w-full"
170
- />
171
- <div className="flex justify-between text-sm text-gray-600">
172
- {question.scaleLabels ? (
173
- Object.entries(question.scaleLabels).map(([value, label]) => (
174
- <span key={value}>{label}</span>
175
- ))
176
- ) : (
177
- <>
178
- <span>{question.minValue || 1}</span>
179
- <span>{question.maxValue || 5}</span>
180
- </>
181
- )}
182
- </div>
183
- </div>
184
- );
185
-
186
- default:
187
- return <p className="text-gray-500">Unsupported question type: {question.type}</p>;
188
- }
189
- };
190
-
191
- // Calculate overall progress
192
- useEffect(() => {
193
- const totalQuestions = questions.length;
194
- const answeredQuestions = Object.keys(answers).length;
195
- const projectInfoComplete = projectName.trim() !== '' ? 1 : 0;
196
-
197
- // Calculate progress percentage (project info counts as one question)
198
- const calculatedProgress = Math.round(((answeredQuestions + projectInfoComplete) / (totalQuestions + 1)) * 100);
199
- setProgress(calculatedProgress);
200
- }, [answers, projectName, questions.length]);
201
-
202
- const validateStep = (step: number) => {
203
- const newErrors: Record<string, string> = {};
204
-
205
- if (step === 0) {
206
- if (!projectName.trim()) {
207
- newErrors.projectName = 'Project name is required';
208
- }
209
- // Description and data controller are optional, so no validation needed
210
- } else {
211
- const category = categoryNames[step - 1];
212
- const categoryQuestions = categories[category];
213
-
214
- categoryQuestions.forEach((question) => {
215
- if (answers[question.id] === undefined) {
216
- newErrors[question.id] = 'Please select an answer for this question';
217
- }
218
- });
219
- }
220
-
221
- setErrors(newErrors);
222
- return Object.keys(newErrors).length === 0;
223
- };
224
-
225
- const handleNext = () => {
226
- if (validateStep(currentStep)) {
227
- setCurrentStep((prev) => prev + 1);
228
- }
229
- };
230
-
231
- const handleBack = () => {
232
- setCurrentStep((prev) => prev - 1);
233
- };
234
-
235
- const handleSubmit = () => {
236
- if (validateStep(currentStep)) {
237
- // Submit with just the answers and project name as per interface definition
238
- onSubmit(answers, projectName);
239
- }
240
- };
241
-
242
- const renderStepContent = () => {
243
- if (currentStep === 0) {
244
- return (
245
- <div className="space-y-6">
246
- <h3 className="text-lg font-medium text-gray-900 dark:text-white">Project Information</h3>
247
-
248
- <div className="bg-blue-50 dark:bg-blue-900/20 p-4 rounded-md mb-6">
249
- <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.
252
- </p>
253
- </div>
254
-
255
- <FormField
256
- id="projectName"
257
- label="Project Name"
258
- required
259
- error={errors.projectName}
260
- >
261
- <Input
262
- id="projectName"
263
- value={projectName}
264
- onChange={(e) => setProjectName(e.target.value)}
265
- placeholder="Enter the name of your project or data processing activity"
266
- />
267
- </FormField>
268
-
269
- <FormField
270
- id="projectDescription"
271
- label="Project Description"
272
- description="Briefly describe the purpose and scope of your project"
273
- >
274
- <TextArea
275
- id="projectDescription"
276
- value={projectDescription}
277
- onChange={(e) => setProjectDescription(e.target.value)}
278
- rows={3}
279
- placeholder="Briefly describe the purpose and scope of your project"
280
- />
281
- </FormField>
282
-
283
- <FormField
284
- id="dataController"
285
- label="Data Controller"
286
- description="Organization responsible for the data"
287
- >
288
- <Input
289
- id="dataController"
290
- value={dataController}
291
- onChange={(e) => setDataController(e.target.value)}
292
- placeholder="Name of the organization responsible for the data"
293
- />
294
- </FormField>
295
-
296
- <FormField
297
- id="assessmentDate"
298
- label="Assessment Date"
299
- >
300
- <Input
301
- type="date"
302
- id="assessmentDate"
303
- value={assessmentDate}
304
- onChange={(e) => setAssessmentDate(e.target.value)}
305
- />
306
- </FormField>
307
- </div>
308
- );
309
- }
310
-
311
- const category = categoryNames[currentStep - 1];
312
- const categoryQuestions = categories[category];
313
-
314
- return (
315
- <div className="space-y-6">
316
- <div className="flex items-center space-x-2">
317
- <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>
319
- </div>
320
- <h3 className="text-lg font-medium text-gray-900 dark:text-white">{category}</h3>
321
- </div>
322
-
323
- <div className="bg-gray-50 dark:bg-gray-800 p-4 rounded-md mb-4">
324
- <p className="text-sm text-gray-600 dark:text-gray-400">
325
- {getCategoryDescription(category)}
326
- </p>
327
- </div>
328
-
329
- {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">
331
- <div className="flex items-start">
332
- <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>
334
- </div>
335
- <label className="block text-sm font-medium text-gray-800 dark:text-gray-200">
336
- {question.text}
337
- </label>
338
- </div>
339
-
340
- <div className="ml-9 space-y-2">
341
- {renderQuestionInput(question)}
342
- </div>
343
-
344
- {errors[question.id] && (
345
- <p className="ml-9 text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
346
- )}
347
- </div>
348
- ))}
349
- </div>
350
- );
351
- };
352
-
353
- // Helper function to get category descriptions
354
- const getCategoryDescription = (category: string): string => {
355
- 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.';
366
- default:
367
- return 'Please answer the following questions about your project.';
368
- }
369
- };
370
-
371
- // Helper function to display risk level indicators
372
- 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'
378
- };
379
-
380
- const labels = {
381
- 1: 'Low Risk',
382
- 2: 'Medium Risk',
383
- 3: 'High Risk',
384
- 4: 'Very High Risk'
385
- };
386
-
387
- return (
388
- <div className="flex items-center ml-auto pl-2">
389
- <Badge variant={variants[value as keyof typeof variants]}>
390
- {labels[value as keyof typeof labels]}
391
- </Badge>
392
- </div>
393
- );
394
- };
395
-
396
- const totalSteps = categoryNames.length + 1; // +1 for the project info step
397
-
398
- return (
399
- <Card className={className}>
400
- <CardHeader>
401
- <CardTitle>Data Protection Impact Assessment</CardTitle>
402
- <p className="text-sm text-gray-500 dark:text-gray-400">
403
- Assess the data protection risks of your project with our DPIA tool.
404
- </p>
405
- </CardHeader>
406
-
407
- <CardContent>
408
- <div className="mb-6">
409
- <div className="flex items-center justify-between mb-2">
410
- <div className="text-sm text-gray-500 dark:text-gray-400">
411
- Step {currentStep + 1} of {totalSteps}
412
- </div>
413
- <div className="text-sm font-medium text-gray-900 dark:text-white">
414
- {currentStep === 0
415
- ? 'Project Information'
416
- : categoryNames[currentStep - 1]}
417
- </div>
418
- </div>
419
- <div className="h-2 w-full bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
420
- <div
421
- className="h-2 bg-blue-600 rounded-full transition-all duration-300 ease-in-out"
422
- style={{ width: `${((currentStep + 1) / totalSteps) * 100}%` }}
423
- />
424
- </div>
425
- <div className="flex justify-between mt-2">
426
- <div className="text-xs text-gray-500 dark:text-gray-400">
427
- Overall completion: {progress}%
428
- </div>
429
- {currentStep > 0 && currentStep < totalSteps && (
430
- <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
434
- </div>
435
- )}
436
- </div>
437
- </div>
438
-
439
- <div className="mb-6">
440
- {renderStepContent()}
441
- </div>
442
-
443
- <div className="flex justify-between pt-4 border-t border-gray-200 dark:border-gray-700">
444
- {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" />
452
- </svg>
453
- Back
454
- </Button>
455
- ) : (
456
- <div>{/* Empty div to maintain layout */}</div>
457
- )}
458
-
459
- {currentStep < totalSteps - 1 ? (
460
- <Button
461
- type="button"
462
- onClick={handleNext}
463
- variant="default"
464
- >
465
- 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" />
468
- </svg>
469
- </Button>
470
- ) : (
471
- <Button
472
- type="button"
473
- onClick={handleSubmit}
474
- variant="default"
475
- >
476
- Submit Assessment
477
- </Button>
478
- )}
479
- </div>
480
- </CardContent>
481
- </Card>
482
- );
483
- }