@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,419 +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 className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
19
- <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
20
- <path fillRule="evenodd" 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" clipRule="evenodd" />
21
- </svg>
22
- );
23
- case 'rectification':
24
- return (
25
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
26
- <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" />
27
- </svg>
28
- );
29
- case 'erasure':
30
- return (
31
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
32
- <path fillRule="evenodd" 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" clipRule="evenodd" />
33
- </svg>
34
- );
35
- case 'restriction':
36
- return (
37
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
38
- <path fillRule="evenodd" 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" clipRule="evenodd" />
39
- </svg>
40
- );
41
- case 'portability':
42
- return (
43
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
44
- <path d="M4 3a2 2 0 100 4h12a2 2 0 100-4H4z" />
45
- <path fillRule="evenodd" 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" clipRule="evenodd" />
46
- </svg>
47
- );
48
- case 'objection':
49
- return (
50
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
51
- <path fillRule="evenodd" 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" clipRule="evenodd" />
52
- </svg>
53
- );
54
- default:
55
- return (
56
- <svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
57
- <path fillRule="evenodd" 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" clipRule="evenodd" />
58
- </svg>
59
- );
60
- }
61
- };
62
-
63
- interface DataSubjectRequestFormProps {
64
- onSubmit: (data: {
65
- requestType: RequestType;
66
- name: string;
67
- email: string;
68
- details: string;
69
- consent: boolean;
70
- }) => void;
71
- className?: string;
72
- }
73
-
74
- export default function DataSubjectRequestForm({
75
- onSubmit,
76
- className = '',
77
- }: DataSubjectRequestFormProps) {
78
- const [formData, setFormData] = useState({
79
- requestType: 'access' as RequestType,
80
- name: '',
81
- email: '',
82
- details: '',
83
- consent: false,
84
- });
85
-
86
- const [errors, setErrors] = useState<Record<string, string>>({});
87
- const [isSubmitting, setIsSubmitting] = useState(false);
88
-
89
- const requestTypeOptions: { value: RequestType; label: string; description: string }[] = [
90
- {
91
- value: 'access',
92
- label: 'Access to Personal Data',
93
- description: 'Request a copy of your personal data that we process',
94
- },
95
- {
96
- value: 'rectification',
97
- label: 'Rectification of Data',
98
- description: 'Request correction of inaccurate personal data',
99
- },
100
- {
101
- value: 'erasure',
102
- label: 'Erasure of Data',
103
- description: 'Request deletion of your personal data ("right to be forgotten")',
104
- },
105
- {
106
- value: 'restriction',
107
- label: 'Restriction of Processing',
108
- description: 'Request to restrict how we process your data',
109
- },
110
- {
111
- value: 'portability',
112
- label: 'Data Portability',
113
- description: 'Request to receive your data in a structured, machine-readable format',
114
- },
115
- {
116
- value: 'objection',
117
- label: 'Object to Processing',
118
- description: 'Object to processing of your personal data',
119
- },
120
- ];
121
-
122
- const handleChange = (
123
- e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
124
- ) => {
125
- const { name, value } = e.target;
126
- setFormData((prev) => ({
127
- ...prev,
128
- [name]: value,
129
- }));
130
-
131
- // Clear error when field is edited
132
- if (errors[name]) {
133
- setErrors((prev) => {
134
- const newErrors = { ...prev };
135
- delete newErrors[name];
136
- return newErrors;
137
- });
138
- }
139
- };
140
-
141
- const validateForm = () => {
142
- const newErrors: Record<string, string> = {};
143
-
144
- if (!formData.name.trim()) {
145
- newErrors.name = 'Name is required';
146
- }
147
-
148
- if (!formData.email.trim()) {
149
- newErrors.email = 'Email is required';
150
- } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
151
- newErrors.email = 'Please enter a valid email address';
152
- }
153
-
154
- if (!formData.details.trim()) {
155
- newErrors.details = 'Please provide details about your request';
156
- }
157
-
158
- if (!formData.consent) {
159
- newErrors.consent = 'You must consent to the processing of your data to submit this request';
160
- }
161
-
162
- setErrors(newErrors);
163
- return Object.keys(newErrors).length === 0;
164
- };
165
-
166
- const handleSubmit = async (e: React.FormEvent) => {
167
- e.preventDefault();
168
-
169
- if (!validateForm()) return;
170
-
171
- setIsSubmitting(true);
172
-
173
- try {
174
- // In a real implementation, this would call an API
175
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
176
-
177
- onSubmit({
178
- requestType: formData.requestType,
179
- name: formData.name,
180
- email: formData.email,
181
- details: formData.details,
182
- consent: formData.consent,
183
- });
184
-
185
- // Reset form after successful submission
186
- setFormData({
187
- requestType: 'access',
188
- name: '',
189
- email: '',
190
- details: '',
191
- consent: false,
192
- });
193
- } catch (error) {
194
- console.error('Error submitting request:', error);
195
- setErrors({
196
- submit: 'An error occurred while submitting your request. Please try again.',
197
- });
198
- } finally {
199
- setIsSubmitting(false);
200
- }
201
- };
202
-
203
- // Track form completion percentage for progress indicator
204
- const [completionPercentage, setCompletionPercentage] = useState(0);
205
-
206
- // Calculate form completion percentage
207
- useEffect(() => {
208
- let filledFields = 0;
209
- const totalFields = 3; // name, email, details
210
-
211
- if (formData.name.trim()) filledFields++;
212
- if (formData.email.trim()) filledFields++;
213
- if (formData.details.trim()) filledFields++;
214
-
215
- setCompletionPercentage(Math.round((filledFields / totalFields) * 100));
216
- }, [formData]);
217
-
218
- return (
219
- <div className={`bg-white dark:bg-gray-800 shadow-lg rounded-lg overflow-hidden ${className}`}>
220
- {/* Header with progress indicator */}
221
- <div className="bg-gradient-to-r from-blue-600 to-indigo-700 p-6 text-white relative">
222
- <h2 className="text-xl font-bold mb-2">
223
- Submit a Data Subject Request
224
- </h2>
225
- <p className="text-blue-100 text-sm mb-4">
226
- Exercise your rights under the Nigeria Data Protection Regulation
227
- </p>
228
-
229
- {/* Progress bar */}
230
- <div className="w-full h-2 bg-blue-800 rounded-full overflow-hidden mt-2">
231
- <div
232
- className="h-full bg-green-400 transition-all duration-300 ease-out"
233
- style={{ width: `${completionPercentage}%` }}
234
- ></div>
235
- </div>
236
- <div className="flex justify-between text-xs text-blue-200 mt-1">
237
- <span>Form Progress</span>
238
- <span>{completionPercentage}% Complete</span>
239
- </div>
240
- </div>
241
-
242
- <form onSubmit={handleSubmit} className="p-6 space-y-6">
243
- {/* Request Type Selection with Icons */}
244
- <div className="mb-8">
245
- <label htmlFor="requestType" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
246
- Request Type
247
- </label>
248
- <div className="grid grid-cols-2 md:grid-cols-3 gap-3 mb-3">
249
- {requestTypeOptions.map((option) => (
250
- <div
251
- key={option.value}
252
- onClick={() => setFormData({...formData, requestType: option.value})}
253
- className={cn(
254
- "cursor-pointer border rounded-lg p-3 transition-all",
255
- formData.requestType === option.value
256
- ? "bg-blue-50 border-blue-500 dark:bg-blue-900/30 dark:border-blue-400"
257
- : "border-gray-200 hover:border-gray-300 dark:border-gray-700 dark:hover:border-gray-600"
258
- )}
259
- >
260
- <div className="flex flex-col items-center text-center">
261
- <div className={cn(
262
- "w-10 h-10 flex items-center justify-center rounded-full mb-2",
263
- formData.requestType === option.value
264
- ? "bg-blue-100 text-blue-600 dark:bg-blue-800 dark:text-blue-300"
265
- : "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
266
- )}>
267
- {getRequestTypeIcon(option.value)}
268
- </div>
269
- <span className="text-sm font-medium">{option.label}</span>
270
- </div>
271
- </div>
272
- ))}
273
- </div>
274
- <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">
275
- <span className="font-medium block mb-1">About this request:</span>
276
- {requestTypeOptions.find((option) => option.value === formData.requestType)?.description}
277
- </p>
278
- </div>
279
-
280
- {/* Personal Information Section */}
281
- <div className="bg-gray-50 dark:bg-gray-800/50 p-4 rounded-lg border border-gray-100 dark:border-gray-700 mb-6">
282
- <h3 className="text-md font-medium text-gray-900 dark:text-white mb-4">Personal Information</h3>
283
-
284
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
285
- <FormField
286
- id="name"
287
- label="Full Name"
288
- required
289
- error={errors.name}
290
- >
291
- <div className="relative">
292
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
293
- <svg className="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
294
- <path fillRule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clipRule="evenodd" />
295
- </svg>
296
- </div>
297
- <Input
298
- id="name"
299
- name="name"
300
- value={formData.name}
301
- onChange={handleChange}
302
- className={cn(
303
- "pl-10",
304
- errors.name && "border-red-300 focus:border-red-500 focus:ring-red-500"
305
- )}
306
- />
307
- </div>
308
- </FormField>
309
-
310
- <FormField
311
- id="email"
312
- label="Email Address"
313
- required
314
- error={errors.email}
315
- >
316
- <div className="relative">
317
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
318
- <svg className="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
319
- <path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
320
- <path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
321
- </svg>
322
- </div>
323
- <Input
324
- type="email"
325
- id="email"
326
- name="email"
327
- value={formData.email}
328
- onChange={handleChange}
329
- className={cn(
330
- "pl-10",
331
- errors.email && "border-red-300 focus:border-red-500 focus:ring-red-500"
332
- )}
333
- />
334
- </div>
335
- </FormField>
336
- </div>
337
- </div>
338
-
339
- {/* Request Details Section */}
340
- <FormField
341
- id="details"
342
- label="Request Details"
343
- required
344
- error={errors.details}
345
- description="Your request will be processed in accordance with the Nigeria Data Protection Regulation (NDPR) and Data Protection Act (DPA)."
346
- >
347
- <div className="relative">
348
- <TextArea
349
- id="details"
350
- name="details"
351
- rows={5}
352
- value={formData.details}
353
- onChange={handleChange}
354
- className={cn(
355
- errors.details && "border-red-300 focus:border-red-500 focus:ring-red-500"
356
- )}
357
- 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."
358
- />
359
- <div className="absolute bottom-2 right-2 text-xs text-gray-400">
360
- {formData.details.length} characters
361
- </div>
362
- </div>
363
- </FormField>
364
-
365
- {/* Consent Checkbox */}
366
- <div className="mt-4">
367
- <Checkbox
368
- id="consent"
369
- name="consent"
370
- checked={formData.consent}
371
- onChange={(e) => setFormData({...formData, consent: e.target.checked})}
372
- label="I consent to the processing of my personal data"
373
- description="By submitting this form, you consent to our processing of your personal data for the purpose of handling your request."
374
- />
375
- {errors.consent && (
376
- <p className="mt-1 text-sm text-red-600 dark:text-red-400">{errors.consent}</p>
377
- )}
378
- </div>
379
-
380
- {errors.submit && (
381
- <div className="rounded-md bg-red-50 dark:bg-red-900/20 p-4 flex items-start">
382
- <svg className="h-5 w-5 text-red-400 mt-0.5 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
383
- <path fillRule="evenodd" 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" clipRule="evenodd" />
384
- </svg>
385
- <p className="text-sm text-red-700 dark:text-red-400">{errors.submit}</p>
386
- </div>
387
- )}
388
-
389
- <div className="flex justify-end pt-4 border-t border-gray-200 dark:border-gray-700">
390
- <Button
391
- type="button"
392
- variant="secondary"
393
- onClick={() => {
394
- setFormData({
395
- requestType: 'access',
396
- name: '',
397
- email: '',
398
- details: '',
399
- consent: false
400
- });
401
- setErrors({});
402
- }}
403
- className="mr-3"
404
- >
405
- Reset Form
406
- </Button>
407
- <LoadingButton
408
- type="submit"
409
- loading={isSubmitting}
410
- loadingText="Processing..."
411
- className="px-6 py-2 bg-blue-600 text-white hover:bg-blue-700 rounded-md font-medium"
412
- >
413
- Submit Request
414
- </LoadingButton>
415
- </div>
416
- </form>
417
- </div>
418
- );
419
- }