@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,373 +0,0 @@
1
- import React from 'react';
2
- import { DPIAResult, DPIASection, DPIARisk } from '../../types/dpia';
3
-
4
- export interface DPIAReportProps {
5
- /**
6
- * The DPIA result to display
7
- */
8
- result: DPIAResult;
9
-
10
- /**
11
- * The sections of the DPIA questionnaire
12
- */
13
- sections: DPIASection[];
14
-
15
- /**
16
- * Whether to show the full report or just a summary
17
- * @default true
18
- */
19
- showFullReport?: boolean;
20
-
21
- /**
22
- * Whether to allow printing the report
23
- * @default true
24
- */
25
- allowPrint?: boolean;
26
-
27
- /**
28
- * Whether to allow exporting the report as PDF
29
- * @default true
30
- */
31
- allowExport?: boolean;
32
-
33
- /**
34
- * Callback function called when the report is exported
35
- */
36
- onExport?: (format: 'pdf' | 'docx' | 'html') => void;
37
-
38
- /**
39
- * Custom CSS class for the report container
40
- */
41
- className?: string;
42
-
43
- /**
44
- * Custom CSS class for the buttons
45
- */
46
- buttonClassName?: string;
47
- }
48
-
49
- export const DPIAReport: React.FC<DPIAReportProps> = ({
50
- result,
51
- sections,
52
- showFullReport = true,
53
- allowPrint = true,
54
- allowExport = true,
55
- onExport,
56
- className = '',
57
- buttonClassName = ''
58
- }) => {
59
- // Format a date from timestamp
60
- const formatDate = (timestamp: number): string => {
61
- return new Date(timestamp).toLocaleDateString('en-GB', {
62
- day: 'numeric',
63
- month: 'long',
64
- year: 'numeric'
65
- });
66
- };
67
-
68
- // Get the section title by ID
69
- const getSectionTitle = (sectionId: string): string => {
70
- const section = sections.find(s => s.id === sectionId);
71
- return section?.title || 'Unknown Section';
72
- };
73
-
74
- // Get the question text by ID
75
- const getQuestionText = (questionId: string): string => {
76
- let questionText = 'Unknown Question';
77
-
78
- sections.forEach(section => {
79
- const question = section.questions.find(q => q.id === questionId);
80
- if (question) {
81
- questionText = question.text;
82
- }
83
- });
84
-
85
- return questionText;
86
- };
87
-
88
- // Get the answer text for a question
89
- const getAnswerText = (questionId: string): string => {
90
- const answer = result.answers[questionId];
91
-
92
- if (answer === undefined || answer === null) {
93
- return 'Not answered';
94
- }
95
-
96
- if (typeof answer === 'boolean') {
97
- return answer ? 'Yes' : 'No';
98
- }
99
-
100
- if (Array.isArray(answer)) {
101
- return answer.join(', ');
102
- }
103
-
104
- return String(answer);
105
- };
106
-
107
- // Handle print button click
108
- const handlePrint = () => {
109
- window.print();
110
- };
111
-
112
- // Handle export button click
113
- const handleExport = (format: 'pdf' | 'docx' | 'html') => {
114
- if (onExport) {
115
- onExport(format);
116
- }
117
- };
118
-
119
- // Render risk level badge
120
- const renderRiskLevelBadge = (level: 'low' | 'medium' | 'high' | 'critical') => {
121
- const colorClasses = {
122
- low: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
123
- medium: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
124
- high: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200',
125
- critical: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
126
- };
127
-
128
- return (
129
- <span className={`px-2 py-1 rounded text-xs font-medium ${colorClasses[level]}`}>
130
- {level.charAt(0).toUpperCase() + level.slice(1)}
131
- </span>
132
- );
133
- };
134
-
135
- return (
136
- <div className={`bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 print:shadow-none print:p-0 ${className}`}>
137
- {/* Report Header */}
138
- <div className="mb-8 border-b border-gray-200 dark:border-gray-700 pb-6 print:pb-4">
139
- <div className="flex justify-between items-start">
140
- <div>
141
- <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
142
- Data Protection Impact Assessment Report
143
- </h1>
144
- <h2 className="text-xl text-gray-700 dark:text-gray-300 mb-4">
145
- {result.title}
146
- </h2>
147
- </div>
148
-
149
- {(allowPrint || allowExport) && (
150
- <div className="flex space-x-2 print:hidden">
151
- {allowPrint && (
152
- <button
153
- onClick={handlePrint}
154
- className={`px-3 py-1 bg-gray-200 text-gray-800 dark:bg-gray-700 dark:text-white rounded hover:bg-gray-300 dark:hover:bg-gray-600 ${buttonClassName}`}
155
- >
156
- <span className="flex items-center">
157
- <svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
158
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
159
- </svg>
160
- Print
161
- </span>
162
- </button>
163
- )}
164
-
165
- {allowExport && (
166
- <div className="relative inline-block">
167
- <button
168
- onClick={() => handleExport('pdf')}
169
- className={`px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 ${buttonClassName}`}
170
- >
171
- <span className="flex items-center">
172
- <svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
173
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
174
- </svg>
175
- Export PDF
176
- </span>
177
- </button>
178
- </div>
179
- )}
180
- </div>
181
- )}
182
- </div>
183
-
184
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
185
- <div>
186
- <p className="text-sm text-gray-500 dark:text-gray-400">
187
- <span className="font-medium">Assessor:</span> {result.assessor.name}, {result.assessor.role}
188
- </p>
189
- <p className="text-sm text-gray-500 dark:text-gray-400">
190
- <span className="font-medium">Contact:</span> {result.assessor.email}
191
- </p>
192
- </div>
193
- <div>
194
- <p className="text-sm text-gray-500 dark:text-gray-400">
195
- <span className="font-medium">Started:</span> {formatDate(result.startedAt)}
196
- </p>
197
- <p className="text-sm text-gray-500 dark:text-gray-400">
198
- <span className="font-medium">Completed:</span> {result.completedAt ? formatDate(result.completedAt) : 'In progress'}
199
- </p>
200
- <p className="text-sm text-gray-500 dark:text-gray-400">
201
- <span className="font-medium">Next review:</span> {result.reviewDate ? formatDate(result.reviewDate) : 'Not scheduled'}
202
- </p>
203
- </div>
204
- </div>
205
- </div>
206
-
207
- {/* Executive Summary */}
208
- <div className="mb-8">
209
- <h2 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
210
- Executive Summary
211
- </h2>
212
-
213
- <div className="bg-gray-50 dark:bg-gray-700 p-4 rounded-md mb-4">
214
- <div className="flex items-center mb-2">
215
- <span className="font-medium mr-2">Overall Risk Level:</span>
216
- {renderRiskLevelBadge(result.overallRiskLevel)}
217
- </div>
218
-
219
- <div className="mb-2">
220
- <span className="font-medium">Conclusion:</span> {result.conclusion}
221
- </div>
222
-
223
- <div>
224
- <span className="font-medium">Can Proceed:</span> {result.canProceed ? 'Yes' : 'No'}
225
- </div>
226
- </div>
227
-
228
- <div>
229
- <h3 className="font-medium text-gray-900 dark:text-white mb-2">
230
- Processing Activity Description
231
- </h3>
232
- <p className="text-gray-700 dark:text-gray-300 mb-4">
233
- {result.processingDescription}
234
- </p>
235
-
236
- {result.recommendations && result.recommendations.length > 0 && (
237
- <div>
238
- <h3 className="font-medium text-gray-900 dark:text-white mb-2">
239
- Key Recommendations
240
- </h3>
241
- <ul className="list-disc pl-5 text-gray-700 dark:text-gray-300">
242
- {result.recommendations.map((recommendation, index) => (
243
- <li key={index}>{recommendation}</li>
244
- ))}
245
- </ul>
246
- </div>
247
- )}
248
- </div>
249
- </div>
250
-
251
- {/* Identified Risks */}
252
- <div className="mb-8">
253
- <h2 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
254
- Identified Risks
255
- </h2>
256
-
257
- {result.risks.length === 0 ? (
258
- <p className="text-gray-700 dark:text-gray-300">No risks identified.</p>
259
- ) : (
260
- <div className="overflow-x-auto">
261
- <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
262
- <thead className="bg-gray-50 dark:bg-gray-700">
263
- <tr>
264
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
265
- Risk
266
- </th>
267
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
268
- Likelihood
269
- </th>
270
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
271
- Impact
272
- </th>
273
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
274
- Risk Level
275
- </th>
276
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
277
- Mitigated
278
- </th>
279
- </tr>
280
- </thead>
281
- <tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
282
- {result.risks.map((risk) => (
283
- <tr key={risk.id}>
284
- <td className="px-6 py-4 whitespace-normal text-sm text-gray-900 dark:text-gray-100">
285
- {risk.description}
286
- </td>
287
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
288
- {risk.likelihood} / 5
289
- </td>
290
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
291
- {risk.impact} / 5
292
- </td>
293
- <td className="px-6 py-4 whitespace-nowrap text-sm">
294
- {renderRiskLevelBadge(risk.level)}
295
- </td>
296
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
297
- {risk.mitigated ? (
298
- <span className="text-green-600 dark:text-green-400">Yes</span>
299
- ) : (
300
- <span className="text-red-600 dark:text-red-400">No</span>
301
- )}
302
- </td>
303
- </tr>
304
- ))}
305
- </tbody>
306
- </table>
307
- </div>
308
- )}
309
- </div>
310
-
311
- {/* Full Assessment Details */}
312
- {showFullReport && (
313
- <div>
314
- <h2 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
315
- Full Assessment Details
316
- </h2>
317
-
318
- {sections.map((section) => {
319
- const sectionQuestions = section.questions.filter(question =>
320
- result.answers[question.id] !== undefined
321
- );
322
-
323
- if (sectionQuestions.length === 0) {
324
- return null;
325
- }
326
-
327
- return (
328
- <div key={section.id} className="mb-6">
329
- <h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
330
- {section.title}
331
- </h3>
332
-
333
- <div className="overflow-x-auto">
334
- <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
335
- <thead className="bg-gray-50 dark:bg-gray-700">
336
- <tr>
337
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
338
- Question
339
- </th>
340
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
341
- Answer
342
- </th>
343
- </tr>
344
- </thead>
345
- <tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
346
- {sectionQuestions.map((question) => (
347
- <tr key={question.id}>
348
- <td className="px-6 py-4 whitespace-normal text-sm text-gray-900 dark:text-gray-100">
349
- {question.text}
350
- </td>
351
- <td className="px-6 py-4 whitespace-normal text-sm text-gray-500 dark:text-gray-400">
352
- {getAnswerText(question.id)}
353
- </td>
354
- </tr>
355
- ))}
356
- </tbody>
357
- </table>
358
- </div>
359
- </div>
360
- );
361
- })}
362
- </div>
363
- )}
364
-
365
- {/* Footer */}
366
- <div className="mt-8 pt-4 border-t border-gray-200 dark:border-gray-700 text-sm text-gray-500 dark:text-gray-400">
367
- <p>This DPIA was conducted in accordance with the Nigeria Data Protection Regulation (NDPR).</p>
368
- <p>DPIA Report Version: {result.version}</p>
369
- <p>Generated on: {new Date().toLocaleDateString()}</p>
370
- </div>
371
- </div>
372
- );
373
- };
@@ -1,174 +0,0 @@
1
- import React from 'react';
2
-
3
- export interface Step {
4
- /**
5
- * Unique identifier for the step
6
- */
7
- id: string;
8
-
9
- /**
10
- * Display label for the step
11
- */
12
- label: string;
13
-
14
- /**
15
- * Optional description for the step
16
- */
17
- description?: string;
18
-
19
- /**
20
- * Whether the step is completed
21
- */
22
- completed: boolean;
23
-
24
- /**
25
- * Whether the step is the current active step
26
- */
27
- active: boolean;
28
-
29
- /**
30
- * Optional icon for the step
31
- */
32
- icon?: React.ReactNode;
33
- }
34
-
35
- export interface StepIndicatorProps {
36
- /**
37
- * Array of steps to display
38
- */
39
- steps: Step[];
40
-
41
- /**
42
- * Callback function called when a step is clicked
43
- */
44
- onStepClick?: (stepId: string) => void;
45
-
46
- /**
47
- * Whether the steps are clickable
48
- * @default true
49
- */
50
- clickable?: boolean;
51
-
52
- /**
53
- * Orientation of the step indicator
54
- * @default "horizontal"
55
- */
56
- orientation?: 'horizontal' | 'vertical';
57
-
58
- /**
59
- * Custom CSS class for the container
60
- */
61
- className?: string;
62
-
63
- /**
64
- * Custom CSS class for the active step
65
- */
66
- activeStepClassName?: string;
67
-
68
- /**
69
- * Custom CSS class for completed steps
70
- */
71
- completedStepClassName?: string;
72
-
73
- /**
74
- * Custom CSS class for incomplete steps
75
- */
76
- incompleteStepClassName?: string;
77
- }
78
-
79
- export const StepIndicator: React.FC<StepIndicatorProps> = ({
80
- steps,
81
- onStepClick,
82
- clickable = true,
83
- orientation = 'horizontal',
84
- className = '',
85
- activeStepClassName = '',
86
- completedStepClassName = '',
87
- incompleteStepClassName = ''
88
- }) => {
89
- const handleStepClick = (stepId: string) => {
90
- if (clickable && onStepClick) {
91
- onStepClick(stepId);
92
- }
93
- };
94
-
95
- const isVertical = orientation === 'vertical';
96
-
97
- return (
98
- <div
99
- className={`${className} ${
100
- isVertical
101
- ? 'flex flex-col space-y-4'
102
- : 'flex items-center justify-between'
103
- }`}
104
- >
105
- {steps.map((step, index) => {
106
- const isLast = index === steps.length - 1;
107
- const stepClassName = step.active
108
- ? `font-medium ${activeStepClassName || 'text-blue-600 dark:text-blue-400'}`
109
- : step.completed
110
- ? `${completedStepClassName || 'text-green-600 dark:text-green-400'}`
111
- : `${incompleteStepClassName || 'text-gray-500 dark:text-gray-400'}`;
112
-
113
- return (
114
- <React.Fragment key={step.id}>
115
- <div
116
- className={`
117
- ${isVertical ? 'flex items-start' : 'flex flex-col items-center'}
118
- ${clickable ? 'cursor-pointer' : ''}
119
- `}
120
- onClick={() => handleStepClick(step.id)}
121
- >
122
- <div className={`
123
- flex items-center justify-center
124
- ${isVertical ? 'mr-4' : ''}
125
- `}>
126
- <div className={`
127
- flex items-center justify-center
128
- w-8 h-8 rounded-full
129
- ${step.active
130
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 border-2 border-blue-600 dark:border-blue-400'
131
- : step.completed
132
- ? 'bg-green-100 dark:bg-green-900 text-green-600 dark:text-green-400 border-2 border-green-600 dark:border-green-400'
133
- : 'bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 border-2 border-gray-300 dark:border-gray-600'
134
- }
135
- `}>
136
- {step.icon ? (
137
- step.icon
138
- ) : step.completed ? (
139
- <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
140
- <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
141
- </svg>
142
- ) : (
143
- <span>{index + 1}</span>
144
- )}
145
- </div>
146
- </div>
147
-
148
- <div className={`
149
- ${isVertical ? 'flex-1' : 'mt-2 text-center'}
150
- `}>
151
- <div className={`text-sm font-medium ${stepClassName}`}>
152
- {step.label}
153
- </div>
154
- {step.description && (
155
- <div className="text-xs text-gray-500 dark:text-gray-400 mt-1">
156
- {step.description}
157
- </div>
158
- )}
159
- </div>
160
- </div>
161
-
162
- {!isLast && (
163
- <div className={`
164
- ${isVertical
165
- ? 'ml-4 h-8 border-l-2 border-gray-300 dark:border-gray-600'
166
- : 'w-full border-t-2 border-gray-300 dark:border-gray-600 hidden sm:block'}
167
- `} />
168
- )}
169
- </React.Fragment>
170
- );
171
- })}
172
- </div>
173
- );
174
- };