@tantainnovative/ndpr-toolkit 1.0.5 → 1.0.6

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 (159) hide show
  1. package/README.md +447 -84
  2. package/dist/index.esm.js +2 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +2 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/types/index.d.ts +35 -0
  7. package/package.json +52 -70
  8. package/CHANGELOG.md +0 -16
  9. package/CNAME +0 -1
  10. package/CONTRIBUTING.md +0 -87
  11. package/RELEASE-NOTES-v1.0.0.md +0 -140
  12. package/RELEASE-NOTES-v1.0.1.md +0 -69
  13. package/SECURITY.md +0 -21
  14. package/components.json +0 -21
  15. package/eslint.config.mjs +0 -16
  16. package/next-env.d.ts +0 -5
  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/dist/index.esm.js +0 -2
  21. package/packages/ndpr-toolkit/dist/index.esm.js.map +0 -1
  22. package/packages/ndpr-toolkit/dist/index.js +0 -2
  23. package/packages/ndpr-toolkit/dist/index.js.map +0 -1
  24. package/packages/ndpr-toolkit/package-lock.json +0 -8197
  25. package/packages/ndpr-toolkit/package.json +0 -71
  26. package/packages/ndpr-toolkit/rollup.config.js +0 -34
  27. package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +0 -701
  28. package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +0 -631
  29. package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +0 -569
  30. package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +0 -496
  31. package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +0 -270
  32. package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +0 -217
  33. package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +0 -206
  34. package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +0 -342
  35. package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +0 -373
  36. package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +0 -174
  37. package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +0 -717
  38. package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +0 -476
  39. package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +0 -620
  40. package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +0 -541
  41. package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +0 -454
  42. package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +0 -333
  43. package/packages/ndpr-toolkit/src/hooks/useBreach.ts +0 -409
  44. package/packages/ndpr-toolkit/src/hooks/useConsent.ts +0 -263
  45. package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +0 -457
  46. package/packages/ndpr-toolkit/src/hooks/useDSR.ts +0 -236
  47. package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +0 -428
  48. package/packages/ndpr-toolkit/src/index.ts +0 -44
  49. package/packages/ndpr-toolkit/src/setupTests.ts +0 -5
  50. package/packages/ndpr-toolkit/src/types/breach.ts +0 -283
  51. package/packages/ndpr-toolkit/src/types/consent.ts +0 -111
  52. package/packages/ndpr-toolkit/src/types/dpia.ts +0 -236
  53. package/packages/ndpr-toolkit/src/types/dsr.ts +0 -192
  54. package/packages/ndpr-toolkit/src/types/index.ts +0 -42
  55. package/packages/ndpr-toolkit/src/types/privacy.ts +0 -246
  56. package/packages/ndpr-toolkit/src/utils/breach.ts +0 -122
  57. package/packages/ndpr-toolkit/src/utils/consent.ts +0 -51
  58. package/packages/ndpr-toolkit/src/utils/dpia.ts +0 -104
  59. package/packages/ndpr-toolkit/src/utils/dsr.ts +0 -77
  60. package/packages/ndpr-toolkit/src/utils/privacy.ts +0 -100
  61. package/packages/ndpr-toolkit/tsconfig.json +0 -23
  62. package/postcss.config.mjs +0 -5
  63. package/public/NDPR TOOLKIT.svg +0 -1
  64. package/public/favicon/android-chrome-192x192.png +0 -0
  65. package/public/favicon/android-chrome-512x512.png +0 -0
  66. package/public/favicon/apple-touch-icon.png +0 -0
  67. package/public/favicon/favicon-16x16.png +0 -0
  68. package/public/favicon/favicon-32x32.png +0 -0
  69. package/public/favicon/site.webmanifest +0 -1
  70. package/public/file.svg +0 -1
  71. package/public/globe.svg +0 -1
  72. package/public/ndpr-toolkit-logo.svg +0 -108
  73. package/public/next.svg +0 -1
  74. package/public/vercel.svg +0 -1
  75. package/public/window.svg +0 -1
  76. package/src/app/accessibility.css +0 -70
  77. package/src/app/favicon.ico +0 -0
  78. package/src/app/globals.css +0 -123
  79. package/src/app/layout.tsx +0 -37
  80. package/src/app/ndpr-demos/breach/page.tsx +0 -354
  81. package/src/app/ndpr-demos/consent/page.tsx +0 -366
  82. package/src/app/ndpr-demos/dpia/page.tsx +0 -495
  83. package/src/app/ndpr-demos/dsr/page.tsx +0 -280
  84. package/src/app/ndpr-demos/page.tsx +0 -73
  85. package/src/app/ndpr-demos/policy/page.tsx +0 -771
  86. package/src/app/page.tsx +0 -452
  87. package/src/components/ErrorBoundary.tsx +0 -90
  88. package/src/components/breach-notification/BreachNotificationForm.tsx +0 -479
  89. package/src/components/consent/ConsentBanner.tsx +0 -193
  90. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +0 -530
  91. package/src/components/dpia/DPIAQuestionnaire.tsx +0 -523
  92. package/src/components/privacy-policy/PolicyGenerator.tsx +0 -1062
  93. package/src/components/privacy-policy/data.ts +0 -98
  94. package/src/components/privacy-policy/shared/CheckboxField.tsx +0 -38
  95. package/src/components/privacy-policy/shared/CheckboxGroup.tsx +0 -85
  96. package/src/components/privacy-policy/shared/FormField.tsx +0 -79
  97. package/src/components/privacy-policy/shared/StepIndicator.tsx +0 -86
  98. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +0 -361
  99. package/src/components/privacy-policy/steps/DataCollectionStep.tsx +0 -231
  100. package/src/components/privacy-policy/steps/DataSharingStep.tsx +0 -418
  101. package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +0 -202
  102. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +0 -226
  103. package/src/components/ui/Badge.tsx +0 -46
  104. package/src/components/ui/Button.tsx +0 -59
  105. package/src/components/ui/Card.tsx +0 -92
  106. package/src/components/ui/Checkbox.tsx +0 -57
  107. package/src/components/ui/FormField.tsx +0 -50
  108. package/src/components/ui/Input.tsx +0 -38
  109. package/src/components/ui/Loading.tsx +0 -201
  110. package/src/components/ui/Select.tsx +0 -42
  111. package/src/components/ui/TextArea.tsx +0 -38
  112. package/src/components/ui/label.tsx +0 -24
  113. package/src/components/ui/switch.tsx +0 -31
  114. package/src/components/ui/tabs.tsx +0 -66
  115. package/src/hooks/useConsent.ts +0 -70
  116. package/src/hooks/useLoadingState.ts +0 -85
  117. package/src/lib/consentService.ts +0 -144
  118. package/src/lib/dpiaQuestions.ts +0 -188
  119. package/src/lib/requestService.ts +0 -79
  120. package/src/lib/sanitize.ts +0 -108
  121. package/src/lib/storage.ts +0 -222
  122. package/src/lib/utils.ts +0 -6
  123. package/src/types/html-to-docx.d.ts +0 -30
  124. package/src/types/index.ts +0 -77
  125. package/tailwind.config.ts +0 -65
  126. package/tsconfig.json +0 -41
  127. /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachNotificationManager.d.ts +0 -0
  128. /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachReportForm.d.ts +0 -0
  129. /package/{packages/ndpr-toolkit/dist → dist}/components/breach/BreachRiskAssessment.d.ts +0 -0
  130. /package/{packages/ndpr-toolkit/dist → dist}/components/breach/RegulatoryReportGenerator.d.ts +0 -0
  131. /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentBanner.d.ts +0 -0
  132. /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentManager.d.ts +0 -0
  133. /package/{packages/ndpr-toolkit/dist → dist}/components/consent/ConsentStorage.d.ts +0 -0
  134. /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/DPIAQuestionnaire.d.ts +0 -0
  135. /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/DPIAReport.d.ts +0 -0
  136. /package/{packages/ndpr-toolkit/dist → dist}/components/dpia/StepIndicator.d.ts +0 -0
  137. /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRDashboard.d.ts +0 -0
  138. /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRRequestForm.d.ts +0 -0
  139. /package/{packages/ndpr-toolkit/dist → dist}/components/dsr/DSRTracker.d.ts +0 -0
  140. /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyExporter.d.ts +0 -0
  141. /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyGenerator.d.ts +0 -0
  142. /package/{packages/ndpr-toolkit/dist → dist}/components/policy/PolicyPreview.d.ts +0 -0
  143. /package/{packages/ndpr-toolkit/dist → dist}/hooks/useBreach.d.ts +0 -0
  144. /package/{packages/ndpr-toolkit/dist → dist}/hooks/useConsent.d.ts +0 -0
  145. /package/{packages/ndpr-toolkit/dist → dist}/hooks/useDPIA.d.ts +0 -0
  146. /package/{packages/ndpr-toolkit/dist → dist}/hooks/useDSR.d.ts +0 -0
  147. /package/{packages/ndpr-toolkit/dist → dist}/hooks/usePrivacyPolicy.d.ts +0 -0
  148. /package/{packages/ndpr-toolkit/dist → dist}/index.d.ts +0 -0
  149. /package/{packages/ndpr-toolkit/dist → dist}/setupTests.d.ts +0 -0
  150. /package/{packages/ndpr-toolkit/dist → dist}/types/breach.d.ts +0 -0
  151. /package/{packages/ndpr-toolkit/dist → dist}/types/consent.d.ts +0 -0
  152. /package/{packages/ndpr-toolkit/dist → dist}/types/dpia.d.ts +0 -0
  153. /package/{packages/ndpr-toolkit/dist → dist}/types/dsr.d.ts +0 -0
  154. /package/{packages/ndpr-toolkit/dist → dist}/types/privacy.d.ts +0 -0
  155. /package/{packages/ndpr-toolkit/dist → dist}/utils/breach.d.ts +0 -0
  156. /package/{packages/ndpr-toolkit/dist → dist}/utils/consent.d.ts +0 -0
  157. /package/{packages/ndpr-toolkit/dist → dist}/utils/dpia.d.ts +0 -0
  158. /package/{packages/ndpr-toolkit/dist → dist}/utils/dsr.d.ts +0 -0
  159. /package/{packages/ndpr-toolkit/dist → dist}/utils/privacy.d.ts +0 -0
@@ -1,530 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useEffect } from "react";
4
- import { RequestType } from "@/types";
5
- import { Input } from "@/components/ui/Input";
6
- import { TextArea } from "@/components/ui/TextArea";
7
- import { Button } from "@/components/ui/Button";
8
- import { FormField } from "@/components/ui/FormField";
9
- import { Checkbox } from "@/components/ui/Checkbox";
10
- import { LoadingButton } from "@/components/ui/Loading";
11
- import { cn } from "@/lib/utils";
12
-
13
- // Helper function to get icons for request types
14
- const getRequestTypeIcon = (requestType: RequestType) => {
15
- switch (requestType) {
16
- case "access":
17
- return (
18
- <svg
19
- className="w-5 h-5"
20
- xmlns="http://www.w3.org/2000/svg"
21
- viewBox="0 0 20 20"
22
- fill="currentColor"
23
- >
24
- <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
25
- <path
26
- fillRule="evenodd"
27
- d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z"
28
- clipRule="evenodd"
29
- />
30
- </svg>
31
- );
32
- case "rectification":
33
- return (
34
- <svg
35
- className="w-5 h-5"
36
- xmlns="http://www.w3.org/2000/svg"
37
- viewBox="0 0 20 20"
38
- fill="currentColor"
39
- >
40
- <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
41
- </svg>
42
- );
43
- case "erasure":
44
- return (
45
- <svg
46
- className="w-5 h-5"
47
- xmlns="http://www.w3.org/2000/svg"
48
- viewBox="0 0 20 20"
49
- fill="currentColor"
50
- >
51
- <path
52
- fillRule="evenodd"
53
- d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
54
- clipRule="evenodd"
55
- />
56
- </svg>
57
- );
58
- case "restriction":
59
- return (
60
- <svg
61
- className="w-5 h-5"
62
- xmlns="http://www.w3.org/2000/svg"
63
- viewBox="0 0 20 20"
64
- fill="currentColor"
65
- >
66
- <path
67
- fillRule="evenodd"
68
- d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
69
- clipRule="evenodd"
70
- />
71
- </svg>
72
- );
73
- case "portability":
74
- return (
75
- <svg
76
- className="w-5 h-5"
77
- xmlns="http://www.w3.org/2000/svg"
78
- viewBox="0 0 20 20"
79
- fill="currentColor"
80
- >
81
- <path d="M4 3a2 2 0 100 4h12a2 2 0 100-4H4z" />
82
- <path
83
- fillRule="evenodd"
84
- d="M3 8h14v7a2 2 0 01-2 2H5a2 2 0 01-2-2V8zm5 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z"
85
- clipRule="evenodd"
86
- />
87
- </svg>
88
- );
89
- case "objection":
90
- return (
91
- <svg
92
- className="w-5 h-5"
93
- xmlns="http://www.w3.org/2000/svg"
94
- viewBox="0 0 20 20"
95
- fill="currentColor"
96
- >
97
- <path
98
- fillRule="evenodd"
99
- d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z"
100
- clipRule="evenodd"
101
- />
102
- </svg>
103
- );
104
- default:
105
- return (
106
- <svg
107
- className="w-5 h-5"
108
- xmlns="http://www.w3.org/2000/svg"
109
- viewBox="0 0 20 20"
110
- fill="currentColor"
111
- >
112
- <path
113
- fillRule="evenodd"
114
- d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z"
115
- clipRule="evenodd"
116
- />
117
- </svg>
118
- );
119
- }
120
- };
121
-
122
- interface DataSubjectRequestFormProps {
123
- onSubmit: (data: {
124
- requestType: RequestType;
125
- name: string;
126
- email: string;
127
- details: string;
128
- consent: boolean;
129
- }) => void;
130
- className?: string;
131
- }
132
-
133
- export default function DataSubjectRequestForm({
134
- onSubmit,
135
- className = "",
136
- }: DataSubjectRequestFormProps) {
137
- const [formData, setFormData] = useState({
138
- requestType: "access" as RequestType,
139
- name: "",
140
- email: "",
141
- details: "",
142
- consent: false,
143
- });
144
-
145
- const [errors, setErrors] = useState<Record<string, string>>({});
146
- const [isSubmitting, setIsSubmitting] = useState(false);
147
-
148
- const requestTypeOptions: {
149
- value: RequestType;
150
- label: string;
151
- description: string;
152
- }[] = [
153
- {
154
- value: "access",
155
- label: "Access to Personal Data",
156
- description: "Request a copy of your personal data that we process",
157
- },
158
- {
159
- value: "rectification",
160
- label: "Rectification of Data",
161
- description: "Request correction of inaccurate personal data",
162
- },
163
- {
164
- value: "erasure",
165
- label: "Erasure of Data",
166
- description:
167
- 'Request deletion of your personal data ("right to be forgotten")',
168
- },
169
- {
170
- value: "restriction",
171
- label: "Restriction of Processing",
172
- description: "Request to restrict how we process your data",
173
- },
174
- {
175
- value: "portability",
176
- label: "Data Portability",
177
- description:
178
- "Request to receive your data in a structured, machine-readable format",
179
- },
180
- {
181
- value: "objection",
182
- label: "Object to Processing",
183
- description: "Object to processing of your personal data",
184
- },
185
- ];
186
-
187
- const handleChange = (
188
- e: React.ChangeEvent<
189
- HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
190
- >,
191
- ) => {
192
- const { name, value } = e.target;
193
- setFormData((prev) => ({
194
- ...prev,
195
- [name]: value,
196
- }));
197
-
198
- // Clear error when field is edited
199
- if (errors[name]) {
200
- setErrors((prev) => {
201
- const newErrors = { ...prev };
202
- delete newErrors[name];
203
- return newErrors;
204
- });
205
- }
206
- };
207
-
208
- const validateForm = () => {
209
- const newErrors: Record<string, string> = {};
210
-
211
- if (!formData.name.trim()) {
212
- newErrors.name = "Name is required";
213
- }
214
-
215
- if (!formData.email.trim()) {
216
- newErrors.email = "Email is required";
217
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
218
- newErrors.email = "Please enter a valid email address";
219
- }
220
-
221
- if (!formData.details.trim()) {
222
- newErrors.details = "Please provide details about your request";
223
- }
224
-
225
- if (!formData.consent) {
226
- newErrors.consent =
227
- "You must consent to the processing of your data to submit this request";
228
- }
229
-
230
- setErrors(newErrors);
231
- return Object.keys(newErrors).length === 0;
232
- };
233
-
234
- const handleSubmit = async (e: React.FormEvent) => {
235
- e.preventDefault();
236
-
237
- if (!validateForm()) return;
238
-
239
- setIsSubmitting(true);
240
-
241
- try {
242
- // In a real implementation, this would call an API
243
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
244
-
245
- onSubmit({
246
- requestType: formData.requestType,
247
- name: formData.name,
248
- email: formData.email,
249
- details: formData.details,
250
- consent: formData.consent,
251
- });
252
-
253
- // Reset form after successful submission
254
- setFormData({
255
- requestType: "access",
256
- name: "",
257
- email: "",
258
- details: "",
259
- consent: false,
260
- });
261
- } catch (error) {
262
- console.error("Error submitting request:", error);
263
- setErrors({
264
- submit:
265
- "An error occurred while submitting your request. Please try again.",
266
- });
267
- } finally {
268
- setIsSubmitting(false);
269
- }
270
- };
271
-
272
- // Track form completion percentage for progress indicator
273
- const [completionPercentage, setCompletionPercentage] = useState(0);
274
-
275
- // Calculate form completion percentage
276
- useEffect(() => {
277
- let filledFields = 0;
278
- const totalFields = 3; // name, email, details
279
-
280
- if (formData.name.trim()) filledFields++;
281
- if (formData.email.trim()) filledFields++;
282
- if (formData.details.trim()) filledFields++;
283
-
284
- setCompletionPercentage(Math.round((filledFields / totalFields) * 100));
285
- }, [formData]);
286
-
287
- return (
288
- <div
289
- className={`bg-white dark:bg-gray-800 shadow-lg rounded-lg overflow-hidden ${className}`}
290
- >
291
- {/* Header with progress indicator */}
292
- <div className="bg-gradient-to-r from-blue-600 to-indigo-700 p-6 text-white relative">
293
- <h2 className="text-xl font-bold mb-2">
294
- Submit a Data Subject Request
295
- </h2>
296
- <p className="text-blue-100 text-sm mb-4">
297
- Exercise your rights under the Nigeria Data Protection Regulation
298
- </p>
299
-
300
- {/* Progress bar */}
301
- <div className="w-full h-2 bg-blue-800 rounded-full overflow-hidden mt-2">
302
- <div
303
- className="h-full bg-green-400 transition-all duration-300 ease-out"
304
- style={{ width: `${completionPercentage}%` }}
305
- ></div>
306
- </div>
307
- <div className="flex justify-between text-xs text-blue-200 mt-1">
308
- <span>Form Progress</span>
309
- <span>{completionPercentage}% Complete</span>
310
- </div>
311
- </div>
312
-
313
- <form onSubmit={handleSubmit} className="p-6 space-y-6">
314
- {/* Request Type Selection with Icons */}
315
- <div className="mb-8">
316
- <label
317
- htmlFor="requestType"
318
- className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
319
- >
320
- Request Type
321
- </label>
322
- <div className="grid grid-cols-2 md:grid-cols-3 gap-3 mb-3">
323
- {requestTypeOptions.map((option) => (
324
- <div
325
- key={option.value}
326
- onClick={() =>
327
- setFormData({ ...formData, requestType: option.value })
328
- }
329
- className={cn(
330
- "cursor-pointer border rounded-lg p-3 transition-all",
331
- formData.requestType === option.value
332
- ? "bg-blue-50 border-blue-500 dark:bg-blue-900/30 dark:border-blue-400"
333
- : "border-gray-200 hover:border-gray-300 dark:border-gray-700 dark:hover:border-gray-600",
334
- )}
335
- >
336
- <div className="flex flex-col items-center text-center">
337
- <div
338
- className={cn(
339
- "w-10 h-10 flex items-center justify-center rounded-full mb-2",
340
- formData.requestType === option.value
341
- ? "bg-blue-100 text-blue-600 dark:bg-blue-800 dark:text-blue-300"
342
- : "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400",
343
- )}
344
- >
345
- {getRequestTypeIcon(option.value)}
346
- </div>
347
- <span className="text-sm font-medium">{option.label}</span>
348
- </div>
349
- </div>
350
- ))}
351
- </div>
352
- <p className="mt-2 text-sm text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800/50 p-3 rounded-lg border border-gray-100 dark:border-gray-700">
353
- <span className="font-medium block mb-1">About this request:</span>
354
- {
355
- requestTypeOptions.find(
356
- (option) => option.value === formData.requestType,
357
- )?.description
358
- }
359
- </p>
360
- </div>
361
-
362
- {/* Personal Information Section */}
363
- <div className="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg border border-gray-100 dark:border-gray-700 mb-6">
364
- <h3 className="text-md font-medium text-gray-900 dark:text-white mb-4">
365
- Personal Information
366
- </h3>
367
-
368
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
369
- <FormField id="name" label="Full Name" required error={errors.name}>
370
- <div className="relative">
371
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
372
- <svg
373
- className="h-5 w-5 text-gray-400"
374
- xmlns="http://www.w3.org/2000/svg"
375
- viewBox="0 0 20 20"
376
- fill="currentColor"
377
- >
378
- <path
379
- fillRule="evenodd"
380
- d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
381
- clipRule="evenodd"
382
- />
383
- </svg>
384
- </div>
385
- <Input
386
- id="name"
387
- name="name"
388
- value={formData.name}
389
- onChange={handleChange}
390
- className={cn(
391
- "pl-10",
392
- errors.name &&
393
- "border-red-300 focus:border-red-500 focus:ring-red-500",
394
- )}
395
- />
396
- </div>
397
- </FormField>
398
-
399
- <FormField
400
- id="email"
401
- label="Email Address"
402
- required
403
- error={errors.email}
404
- >
405
- <div className="relative">
406
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
407
- <svg
408
- className="h-5 w-5 text-gray-400"
409
- xmlns="http://www.w3.org/2000/svg"
410
- viewBox="0 0 20 20"
411
- fill="currentColor"
412
- >
413
- <path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
414
- <path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
415
- </svg>
416
- </div>
417
- <Input
418
- type="email"
419
- id="email"
420
- name="email"
421
- value={formData.email}
422
- onChange={handleChange}
423
- className={cn(
424
- "pl-10",
425
- errors.email &&
426
- "border-red-300 focus:border-red-500 focus:ring-red-500",
427
- )}
428
- />
429
- </div>
430
- </FormField>
431
- </div>
432
- </div>
433
-
434
- {/* Request Details Section */}
435
- <FormField
436
- id="details"
437
- label="Request Details"
438
- required
439
- error={errors.details}
440
- description="Your request will be processed in accordance with the Nigeria Data Protection Regulation (NDPR) and Data Protection Act (DPA)."
441
- >
442
- <div className="relative">
443
- <TextArea
444
- id="details"
445
- name="details"
446
- rows={5}
447
- value={formData.details}
448
- onChange={handleChange}
449
- className={cn(
450
- errors.details &&
451
- "border-red-300 focus:border-red-500 focus:ring-red-500",
452
- )}
453
- placeholder="Please provide specific details about your request. For example, what personal data you would like to access, what corrections you need to make, etc."
454
- />
455
- <div className="absolute bottom-2 right-2 text-xs text-gray-400">
456
- {formData.details.length} characters
457
- </div>
458
- </div>
459
- </FormField>
460
-
461
- {/* Consent Checkbox */}
462
- <div className="mt-4">
463
- <Checkbox
464
- id="consent"
465
- name="consent"
466
- checked={formData.consent}
467
- onChange={(e) =>
468
- setFormData({ ...formData, consent: e.target.checked })
469
- }
470
- label="I consent to the processing of my personal data"
471
- description="By submitting this form, you consent to our processing of your personal data for the purpose of handling your request."
472
- />
473
- {errors.consent && (
474
- <p className="mt-1 text-sm text-red-600 dark:text-red-400">
475
- {errors.consent}
476
- </p>
477
- )}
478
- </div>
479
-
480
- {errors.submit && (
481
- <div className="rounded-md bg-red-50 dark:bg-red-900/20 p-4 flex items-start">
482
- <svg
483
- className="h-5 w-5 text-red-400 mt-0.5 mr-2"
484
- xmlns="http://www.w3.org/2000/svg"
485
- viewBox="0 0 20 20"
486
- fill="currentColor"
487
- >
488
- <path
489
- fillRule="evenodd"
490
- d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
491
- clipRule="evenodd"
492
- />
493
- </svg>
494
- <p className="text-sm text-red-700 dark:text-red-400">
495
- {errors.submit}
496
- </p>
497
- </div>
498
- )}
499
-
500
- <div className="flex justify-end pt-4 border-t border-gray-200 dark:border-gray-700">
501
- <Button
502
- type="button"
503
- variant="secondary"
504
- onClick={() => {
505
- setFormData({
506
- requestType: "access",
507
- name: "",
508
- email: "",
509
- details: "",
510
- consent: false,
511
- });
512
- setErrors({});
513
- }}
514
- className="mr-3"
515
- >
516
- Reset Form
517
- </Button>
518
- <LoadingButton
519
- type="submit"
520
- loading={isSubmitting}
521
- loadingText="Processing..."
522
- className="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 rounded-md font-medium"
523
- >
524
- Submit Request
525
- </LoadingButton>
526
- </div>
527
- </form>
528
- </div>
529
- );
530
- }