@tantainnovative/ndpr-toolkit 1.0.2 → 1.0.3

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 (212) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/.eslintrc.json +10 -0
  3. package/.github/workflows/ci.yml +36 -0
  4. package/.github/workflows/nextjs.yml +104 -0
  5. package/.husky/commit-msg +4 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.lintstagedrc.js +4 -0
  8. package/.nvmrc +1 -0
  9. package/.versionrc +17 -0
  10. package/CHANGELOG.md +16 -0
  11. package/CLAUDE.md +90 -0
  12. package/CNAME +1 -0
  13. package/CONTRIBUTING.md +87 -0
  14. package/README.md +84 -447
  15. package/RELEASE-NOTES-v1.0.0.md +140 -0
  16. package/RELEASE-NOTES-v1.0.1.md +69 -0
  17. package/SECURITY.md +21 -0
  18. package/commitlint.config.js +36 -0
  19. package/components.json +21 -0
  20. package/eslint.config.mjs +16 -0
  21. package/jest.config.js +31 -0
  22. package/jest.setup.js +15 -0
  23. package/next.config.js +15 -0
  24. package/next.config.ts +62 -0
  25. package/package.json +70 -52
  26. package/packages/ndpr-toolkit/README.md +467 -0
  27. package/packages/ndpr-toolkit/jest.config.js +23 -0
  28. package/packages/ndpr-toolkit/package-lock.json +8197 -0
  29. package/packages/ndpr-toolkit/package.json +71 -0
  30. package/packages/ndpr-toolkit/rollup.config.js +34 -0
  31. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
  32. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
  33. package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
  34. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
  35. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
  36. package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
  37. package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
  38. package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
  39. package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
  40. package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
  41. package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
  42. package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
  43. package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
  44. package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
  45. package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
  46. package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
  47. package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
  48. package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
  49. package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
  50. package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
  51. package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
  52. package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
  53. package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
  54. package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
  55. package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
  56. package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
  57. package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
  58. package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
  59. package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
  60. package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
  61. package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
  62. package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
  63. package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
  64. package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
  65. package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +13 -0
  66. package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
  67. package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
  68. package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
  69. package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
  70. package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
  71. package/packages/ndpr-toolkit/src/types/index.ts +42 -0
  72. package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
  73. package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
  74. package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
  75. package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
  76. package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
  77. package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
  78. package/packages/ndpr-toolkit/tsconfig.json +23 -0
  79. package/postcss.config.mjs +5 -0
  80. package/public/NDPR TOOLKIT.svg +1 -0
  81. package/public/favicon/android-chrome-192x192.png +0 -0
  82. package/public/favicon/android-chrome-512x512.png +0 -0
  83. package/public/favicon/apple-touch-icon.png +0 -0
  84. package/public/favicon/favicon-16x16.png +0 -0
  85. package/public/favicon/favicon-32x32.png +0 -0
  86. package/public/favicon/site.webmanifest +1 -0
  87. package/public/file.svg +1 -0
  88. package/public/globe.svg +1 -0
  89. package/public/ndpr-toolkit-logo.svg +108 -0
  90. package/public/next.svg +1 -0
  91. package/public/vercel.svg +1 -0
  92. package/public/window.svg +1 -0
  93. package/src/__tests__/example.test.ts +13 -0
  94. package/src/__tests__/requestService.test.ts +57 -0
  95. package/src/app/accessibility.css +70 -0
  96. package/src/app/docs/components/DocLayout.tsx +267 -0
  97. package/src/app/docs/components/breach-notification/page.tsx +797 -0
  98. package/src/app/docs/components/consent-management/page.tsx +576 -0
  99. package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
  100. package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
  101. package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
  102. package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
  103. package/src/app/docs/components/hooks/page.tsx +305 -0
  104. package/src/app/docs/components/page.tsx +84 -0
  105. package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
  106. package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
  107. package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
  108. package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
  109. package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
  110. package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
  111. package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
  112. package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
  113. package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
  114. package/src/app/docs/guides/managing-consent/page.tsx +738 -0
  115. package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
  116. package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
  117. package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
  118. package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
  119. package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
  120. package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
  121. package/src/app/docs/guides/page.tsx +67 -0
  122. package/src/app/docs/layout.tsx +15 -0
  123. package/src/app/docs/metadata.ts +31 -0
  124. package/src/app/docs/page.tsx +572 -0
  125. package/src/app/favicon.ico +0 -0
  126. package/src/app/globals.css +123 -0
  127. package/src/app/layout.tsx +37 -0
  128. package/src/app/ndpr-demos/breach/page.tsx +354 -0
  129. package/src/app/ndpr-demos/consent/page.tsx +366 -0
  130. package/src/app/ndpr-demos/dpia/page.tsx +495 -0
  131. package/src/app/ndpr-demos/dsr/page.tsx +280 -0
  132. package/src/app/ndpr-demos/page.tsx +73 -0
  133. package/src/app/ndpr-demos/policy/page.tsx +771 -0
  134. package/src/app/page.tsx +452 -0
  135. package/src/components/ErrorBoundary.tsx +90 -0
  136. package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
  137. package/src/components/consent/ConsentBanner.tsx +159 -0
  138. package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
  139. package/src/components/docs/DocLayout.tsx +289 -0
  140. package/src/components/docs/index.ts +2 -0
  141. package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
  142. package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
  143. package/src/components/privacy-policy/data.ts +98 -0
  144. package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
  145. package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
  146. package/src/components/privacy-policy/shared/FormField.tsx +79 -0
  147. package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
  148. package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
  149. package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
  150. package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
  151. package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
  152. package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
  153. package/src/components/ui/Badge.tsx +46 -0
  154. package/src/components/ui/Button.tsx +59 -0
  155. package/src/components/ui/Card.tsx +92 -0
  156. package/src/components/ui/Checkbox.tsx +57 -0
  157. package/src/components/ui/FormField.tsx +50 -0
  158. package/src/components/ui/Input.tsx +38 -0
  159. package/src/components/ui/Loading.tsx +201 -0
  160. package/src/components/ui/Select.tsx +42 -0
  161. package/src/components/ui/TextArea.tsx +38 -0
  162. package/src/components/ui/label.tsx +24 -0
  163. package/src/components/ui/switch.tsx +31 -0
  164. package/src/components/ui/tabs.tsx +66 -0
  165. package/src/hooks/useConsent.ts +64 -0
  166. package/src/hooks/useLoadingState.ts +85 -0
  167. package/src/lib/consentService.ts +137 -0
  168. package/src/lib/dpiaQuestions.ts +148 -0
  169. package/src/lib/requestService.ts +75 -0
  170. package/src/lib/sanitize.ts +108 -0
  171. package/src/lib/storage.ts +222 -0
  172. package/src/lib/utils.ts +6 -0
  173. package/src/types/html-to-docx.d.ts +30 -0
  174. package/src/types/index.ts +72 -0
  175. package/tailwind.config.ts +65 -0
  176. package/tsconfig.json +41 -0
  177. package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
  178. package/dist/components/breach/BreachReportForm.d.ts +0 -66
  179. package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
  180. package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
  181. package/dist/components/consent/ConsentBanner.d.ts +0 -79
  182. package/dist/components/consent/ConsentManager.d.ts +0 -73
  183. package/dist/components/consent/ConsentStorage.d.ts +0 -41
  184. package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
  185. package/dist/components/dpia/DPIAReport.d.ts +0 -40
  186. package/dist/components/dpia/StepIndicator.d.ts +0 -64
  187. package/dist/components/dsr/DSRDashboard.d.ts +0 -58
  188. package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
  189. package/dist/components/dsr/DSRTracker.d.ts +0 -56
  190. package/dist/components/policy/PolicyExporter.d.ts +0 -65
  191. package/dist/components/policy/PolicyGenerator.d.ts +0 -54
  192. package/dist/components/policy/PolicyPreview.d.ts +0 -71
  193. package/dist/hooks/useBreach.d.ts +0 -97
  194. package/dist/hooks/useConsent.d.ts +0 -63
  195. package/dist/hooks/useDPIA.d.ts +0 -92
  196. package/dist/hooks/useDSR.d.ts +0 -72
  197. package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
  198. package/dist/index.esm.js +0 -2
  199. package/dist/index.esm.js.map +0 -1
  200. package/dist/index.js +0 -2
  201. package/dist/index.js.map +0 -1
  202. package/dist/setupTests.d.ts +0 -2
  203. package/dist/types/breach.d.ts +0 -239
  204. package/dist/types/consent.d.ts +0 -95
  205. package/dist/types/dpia.d.ts +0 -196
  206. package/dist/types/dsr.d.ts +0 -162
  207. package/dist/types/privacy.d.ts +0 -204
  208. package/dist/utils/breach.d.ts +0 -14
  209. package/dist/utils/consent.d.ts +0 -10
  210. package/dist/utils/dpia.d.ts +0 -12
  211. package/dist/utils/dsr.d.ts +0 -11
  212. package/dist/utils/privacy.d.ts +0 -12
@@ -0,0 +1,172 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Card, CardContent } from '@/components/ui/Card';
5
+
6
+ interface PolicyPreviewStepProps {
7
+ formData: {
8
+ organizationName: string;
9
+ organizationContact: string;
10
+ organizationWebsite: string;
11
+ organizationAddress: string;
12
+ dataCollectionPurposes: string[];
13
+ dataRetentionPeriod: string;
14
+ thirdPartySharing: boolean;
15
+ thirdParties: string[];
16
+ securityMeasures: string[];
17
+ cookiesUsed: boolean;
18
+ cookieTypes: string[];
19
+ internationalTransfers: boolean;
20
+ transferCountries: string[];
21
+ customSections: { title: string; template: string }[];
22
+ hasDPO: boolean;
23
+ dpoContact: string;
24
+ includeNDPRCompliance: boolean;
25
+ policyEffectiveDate: string;
26
+ };
27
+ previewRef: React.RefObject<HTMLDivElement | null>;
28
+ }
29
+
30
+ export default function PolicyPreviewStep({
31
+ formData,
32
+ previewRef,
33
+ }: PolicyPreviewStepProps) {
34
+ return (
35
+ <div className="space-y-8">
36
+ <Card className="transition-all duration-200">
37
+ <CardContent className="p-6">
38
+ <div className="flex items-center justify-between mb-4 pb-3 border-b border-gray-200 dark:border-gray-700">
39
+ <h3 className="text-lg font-medium text-gray-900 dark:text-white flex items-center">
40
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2 text-indigo-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
41
+ <path fillRule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clipRule="evenodd" />
42
+ </svg>
43
+ Privacy Policy Preview
44
+ </h3>
45
+ <span className="text-xs text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded-full">
46
+ Preview Mode
47
+ </span>
48
+ </div>
49
+
50
+ <div ref={previewRef} className="prose dark:prose-invert prose-indigo max-w-none text-base prose-headings:text-gray-800 dark:prose-headings:text-gray-100 prose-p:text-gray-600 dark:prose-p:text-gray-300 prose-li:text-gray-600 dark:prose-li:text-gray-300 prose-h1:text-2xl prose-h1:font-bold prose-h2:text-xl prose-h2:font-semibold prose-h2:border-b prose-h2:border-gray-200 dark:prose-h2:border-gray-700 prose-h2:pb-2 prose-h2:mt-8 overflow-auto">
51
+ <h1>{formData.organizationName} Privacy Policy</h1>
52
+
53
+ <h2>Introduction</h2>
54
+ <p>
55
+ This Privacy Policy describes how {formData.organizationName} collects, uses, and discloses your personal information.
56
+ {formData.includeNDPRCompliance && ' This policy is compliant with the Nigeria Data Protection Regulation (NDPR) and the Data Protection Act (DPA).'}
57
+ </p>
58
+
59
+ <h2>About Us</h2>
60
+ <p>
61
+ {formData.organizationName}
62
+ {formData.organizationAddress && ` is located at ${formData.organizationAddress}`}.
63
+ {formData.organizationWebsite && ` Our website is ${formData.organizationWebsite}.`}
64
+ </p>
65
+
66
+ <h2>Information We Collect</h2>
67
+ <p>We collect and process your personal data for the following purposes:</p>
68
+ <ul>
69
+ {(formData.dataCollectionPurposes || []).map((purpose, index) => (
70
+ <li key={`purpose-${index}`}>{purpose}</li>
71
+ ))}
72
+ </ul>
73
+
74
+ <h2>Data Retention</h2>
75
+ <p>
76
+ We will retain your personal data for {formData.dataRetentionPeriod || 'a period appropriate to the purpose'},
77
+ or for as long as necessary to fulfill the purposes for which it was collected.
78
+ </p>
79
+
80
+ {formData.thirdPartySharing && (formData.thirdParties || []).length > 0 && (
81
+ <>
82
+ <h2>Third-Party Sharing</h2>
83
+ <p>We may share your personal information with the following third parties:</p>
84
+ <ul>
85
+ {(formData.thirdParties || []).map((party, index) => (
86
+ <li key={`party-${index}`}>{party}</li>
87
+ ))}
88
+ </ul>
89
+ </>
90
+ )}
91
+
92
+ {formData.cookiesUsed && (formData.cookieTypes || []).length > 0 && (
93
+ <>
94
+ <h2>Cookies and Tracking Technologies</h2>
95
+ <p>Our website uses cookies and similar tracking technologies. We use the following types of cookies:</p>
96
+ <ul>
97
+ {(formData.cookieTypes || []).map((type, index) => (
98
+ <li key={`type-${index}`}>{type}</li>
99
+ ))}
100
+ </ul>
101
+ </>
102
+ )}
103
+
104
+ {formData.internationalTransfers && (formData.transferCountries || []).length > 0 && (
105
+ <>
106
+ <h2>International Data Transfers</h2>
107
+ <p>We may transfer your personal data to the following countries outside Nigeria:</p>
108
+ <ul>
109
+ {(formData.transferCountries || []).map((country, index) => (
110
+ <li key={`country-${index}`}>{country}</li>
111
+ ))}
112
+ </ul>
113
+ <p>We ensure appropriate safeguards are in place to protect your data when transferred internationally.</p>
114
+ </>
115
+ )}
116
+
117
+ {(formData.securityMeasures || []).length > 0 && (
118
+ <>
119
+ <h2>Security Measures</h2>
120
+ <p>We implement the following security measures to protect your personal data:</p>
121
+ <ul>
122
+ {(formData.securityMeasures || []).map((measure, index) => (
123
+ <li key={`measure-${index}`}>{measure}</li>
124
+ ))}
125
+ </ul>
126
+ </>
127
+ )}
128
+
129
+ <h2>Your Rights</h2>
130
+ <p>
131
+ {formData.includeNDPRCompliance
132
+ ? 'Under the NDPR and DPA, you have the following rights:'
133
+ : 'You have the following rights regarding your personal data:'}
134
+ </p>
135
+ <ul>
136
+ <li>Right to access your personal data</li>
137
+ <li>Right to rectify inaccurate personal data</li>
138
+ <li>Right to erasure (&quot;right to be forgotten&quot;)</li>
139
+ <li>Right to restrict processing</li>
140
+ <li>Right to object to processing</li>
141
+ <li>Right to data portability</li>
142
+ </ul>
143
+ <p>To exercise these rights, please contact us at {formData.organizationContact}.</p>
144
+
145
+ {(formData.customSections || []).length > 0 && (formData.customSections || []).map((section, index) => (
146
+ <div key={`section-${index}`}>
147
+ <h2>{section.title}</h2>
148
+ <p>{section.template}</p>
149
+ </div>
150
+ ))}
151
+
152
+ {formData.hasDPO && formData.dpoContact && (
153
+ <>
154
+ <h2>Data Protection Officer</h2>
155
+ <p>You can contact our Data Protection Officer at {formData.dpoContact}.</p>
156
+ </>
157
+ )}
158
+
159
+ <h2>Contact Us</h2>
160
+ <p>
161
+ If you have any questions about this Privacy Policy, please contact us at {formData.organizationContact}.
162
+ </p>
163
+
164
+ <p className="text-gray-500 dark:text-gray-400 mt-4">
165
+ Last Updated: {formData.policyEffectiveDate || new Date().toISOString().split('T')[0]}
166
+ </p>
167
+ </div>
168
+ </CardContent>
169
+ </Card>
170
+ </div>
171
+ );
172
+ }
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-gray-700 text-white dark:bg-gray-500 focus:ring-gray-500",
13
+ primary: "bg-blue-700 text-white dark:bg-blue-600 focus:ring-blue-500",
14
+ secondary: "bg-gray-600 text-white dark:bg-gray-400 focus:ring-gray-500",
15
+ success: "bg-green-700 text-white dark:bg-green-600 focus:ring-green-500",
16
+ danger: "bg-red-700 text-white dark:bg-red-600 focus:ring-red-500",
17
+ warning: "bg-amber-700 text-white dark:bg-amber-600 focus:ring-amber-500",
18
+ info: "bg-blue-700 text-white dark:bg-blue-600 focus:ring-blue-500",
19
+ outline: "border-2 border-gray-700 dark:border-gray-300 text-gray-900 dark:text-gray-100 focus:ring-gray-500",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ }
26
+ );
27
+
28
+ export interface BadgeProps
29
+ extends React.HTMLAttributes<HTMLDivElement>,
30
+ VariantProps<typeof badgeVariants> {}
31
+
32
+ const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
33
+ ({ className, variant, ...props }, ref) => {
34
+ return (
35
+ <div
36
+ ref={ref}
37
+ className={cn(badgeVariants({ variant }), className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+ );
43
+
44
+ Badge.displayName = "Badge";
45
+
46
+ export { Badge, badgeVariants };
@@ -0,0 +1,59 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16
+ outline:
17
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
20
+ ghost:
21
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22
+ link: "text-primary underline-offset-4 hover:underline",
23
+ },
24
+ size: {
25
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
26
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28
+ icon: "size-9",
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ variant: "default",
33
+ size: "default",
34
+ },
35
+ }
36
+ )
37
+
38
+ function Button({
39
+ className,
40
+ variant,
41
+ size,
42
+ asChild = false,
43
+ ...props
44
+ }: React.ComponentProps<"button"> &
45
+ VariantProps<typeof buttonVariants> & {
46
+ asChild?: boolean
47
+ }) {
48
+ const Comp = asChild ? Slot : "button"
49
+
50
+ return (
51
+ <Comp
52
+ data-slot="button"
53
+ className={cn(buttonVariants({ variant, size, className }))}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ export { Button, buttonVariants }
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ }
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ export interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
7
+ label?: string;
8
+ description?: string;
9
+ }
10
+
11
+ const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
12
+ ({ className, label, description, ...props }, ref) => {
13
+ const generatedId = React.useId();
14
+ const id = props.id || generatedId;
15
+
16
+ return (
17
+ <div className="flex items-start">
18
+ <div className="flex items-center h-5">
19
+ <input
20
+ type="checkbox"
21
+ id={id}
22
+ className={cn(
23
+ "h-4 w-4 rounded border-gray-300 text-blue-600",
24
+ "focus:ring-blue-500 focus:ring-offset-0",
25
+ "disabled:opacity-70 disabled:cursor-not-allowed",
26
+ className
27
+ )}
28
+ ref={ref}
29
+ {...props}
30
+ />
31
+ </div>
32
+ {(label || description) && (
33
+ <div className="ml-3 text-sm">
34
+ {label && (
35
+ <label
36
+ htmlFor={id}
37
+ className={cn(
38
+ "font-medium text-gray-900 dark:text-white",
39
+ props.disabled && "text-gray-500 dark:text-gray-400"
40
+ )}
41
+ >
42
+ {label}
43
+ </label>
44
+ )}
45
+ {description && (
46
+ <p className="text-gray-500 dark:text-gray-400">{description}</p>
47
+ )}
48
+ </div>
49
+ )}
50
+ </div>
51
+ );
52
+ }
53
+ );
54
+
55
+ Checkbox.displayName = "Checkbox";
56
+
57
+ export { Checkbox };
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ interface FormFieldProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ id: string;
8
+ label: string;
9
+ description?: string;
10
+ error?: string;
11
+ required?: boolean;
12
+ }
13
+
14
+ const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
15
+ ({ id, label, description, error, required = false, className, children, ...props }, ref) => {
16
+ return (
17
+ <div ref={ref} className={cn("mb-4", className)} {...props}>
18
+ <div className="mb-1.5">
19
+ <label
20
+ htmlFor={id}
21
+ className="block text-sm font-medium text-gray-900 dark:text-gray-100"
22
+ >
23
+ {label}
24
+ {required && <span className="ml-1 text-red-500">*</span>}
25
+ </label>
26
+
27
+ {description && (
28
+ <p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
29
+ {description}
30
+ </p>
31
+ )}
32
+ </div>
33
+
34
+ <div className="mt-1">
35
+ {children}
36
+ </div>
37
+
38
+ {error && (
39
+ <p className="mt-1.5 text-xs text-red-600 dark:text-red-400">
40
+ {error}
41
+ </p>
42
+ )}
43
+ </div>
44
+ );
45
+ }
46
+ );
47
+
48
+ FormField.displayName = "FormField";
49
+
50
+ export { FormField };
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ // This interface extends the standard HTML input attributes
8
+ // and allows for additional props to be added in the future
9
+ /* eslint-disable-next-line @typescript-eslint/no-empty-object-type */
10
+ export interface InputProps
11
+ extends React.InputHTMLAttributes<HTMLInputElement> {
12
+ // Custom props can be added here
13
+ }
14
+
15
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
16
+ ({ className, type, ...props }, ref) => {
17
+ return (
18
+ <input
19
+ type={type}
20
+ className={cn(
21
+ "block w-full px-3 py-2 text-base",
22
+ "border border-gray-300 dark:border-gray-600 rounded-md shadow-sm",
23
+ "bg-white dark:bg-gray-800 text-gray-900 dark:text-white",
24
+ "placeholder:text-gray-400 dark:placeholder:text-gray-500",
25
+ "focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400",
26
+ "disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50 dark:disabled:bg-gray-900",
27
+ className
28
+ )}
29
+ ref={ref}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+ );
35
+
36
+ Input.displayName = "Input";
37
+
38
+ export { Input };