@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.
- package/next-env.d.ts +5 -0
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -20
- package/.eslintrc.json +0 -10
- package/.github/workflows/ci.yml +0 -36
- package/.github/workflows/nextjs.yml +0 -104
- package/.husky/commit-msg +0 -4
- package/.husky/pre-commit +0 -4
- package/.lintstagedrc.js +0 -4
- package/.nvmrc +0 -1
- package/.versionrc +0 -17
- package/CLAUDE.md +0 -90
- package/commitlint.config.js +0 -36
- package/eslint.config.mjs +0 -16
- package/jest.config.js +0 -31
- package/jest.setup.js +0 -15
- package/next.config.js +0 -15
- package/next.config.ts +0 -62
- package/packages/ndpr-toolkit/README.md +0 -467
- package/packages/ndpr-toolkit/jest.config.js +0 -23
- package/packages/ndpr-toolkit/package-lock.json +0 -8197
- package/packages/ndpr-toolkit/package.json +0 -71
- package/packages/ndpr-toolkit/rollup.config.js +0 -34
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +0 -119
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +0 -122
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +0 -270
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +0 -199
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +0 -224
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +0 -104
- package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +0 -161
- package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +0 -330
- package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +0 -149
- package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +0 -88
- package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +0 -160
- package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +0 -110
- package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +0 -97
- package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +0 -701
- package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +0 -631
- package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +0 -569
- package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +0 -496
- package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +0 -270
- package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +0 -217
- package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +0 -206
- package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +0 -342
- package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +0 -373
- package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +0 -174
- package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +0 -717
- package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +0 -476
- package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +0 -620
- package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +0 -541
- package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +0 -454
- package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +0 -333
- package/packages/ndpr-toolkit/src/hooks/useBreach.ts +0 -409
- package/packages/ndpr-toolkit/src/hooks/useConsent.ts +0 -263
- package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +0 -457
- package/packages/ndpr-toolkit/src/hooks/useDSR.ts +0 -236
- package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +0 -428
- package/packages/ndpr-toolkit/src/index.ts +0 -44
- package/packages/ndpr-toolkit/src/setupTests.ts +0 -5
- package/packages/ndpr-toolkit/src/types/breach.ts +0 -283
- package/packages/ndpr-toolkit/src/types/consent.ts +0 -111
- package/packages/ndpr-toolkit/src/types/dpia.ts +0 -236
- package/packages/ndpr-toolkit/src/types/dsr.ts +0 -192
- package/packages/ndpr-toolkit/src/types/index.ts +0 -42
- package/packages/ndpr-toolkit/src/types/privacy.ts +0 -246
- package/packages/ndpr-toolkit/src/utils/breach.ts +0 -122
- package/packages/ndpr-toolkit/src/utils/consent.ts +0 -51
- package/packages/ndpr-toolkit/src/utils/dpia.ts +0 -104
- package/packages/ndpr-toolkit/src/utils/dsr.ts +0 -77
- package/packages/ndpr-toolkit/src/utils/privacy.ts +0 -100
- package/packages/ndpr-toolkit/tsconfig.json +0 -23
- package/postcss.config.mjs +0 -5
- package/src/__tests__/example.test.ts +0 -13
- package/src/__tests__/requestService.test.ts +0 -57
- package/src/app/accessibility.css +0 -70
- package/src/app/docs/components/DocLayout.tsx +0 -267
- package/src/app/docs/components/breach-notification/page.tsx +0 -797
- package/src/app/docs/components/consent-management/page.tsx +0 -576
- package/src/app/docs/components/data-subject-rights/page.tsx +0 -511
- package/src/app/docs/components/dpia-questionnaire/layout.tsx +0 -15
- package/src/app/docs/components/dpia-questionnaire/metadata.ts +0 -31
- package/src/app/docs/components/dpia-questionnaire/page.tsx +0 -666
- package/src/app/docs/components/hooks/page.tsx +0 -305
- package/src/app/docs/components/page.tsx +0 -84
- package/src/app/docs/components/privacy-policy-generator/page.tsx +0 -634
- package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +0 -123
- package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +0 -328
- package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +0 -28
- package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +0 -91
- package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +0 -118
- package/src/app/docs/guides/breach-notification-process/page.tsx +0 -39
- package/src/app/docs/guides/conducting-dpia/page.tsx +0 -593
- package/src/app/docs/guides/data-subject-requests/page.tsx +0 -666
- package/src/app/docs/guides/managing-consent/page.tsx +0 -738
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +0 -296
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +0 -145
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +0 -33
- package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +0 -99
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +0 -159
- package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +0 -38
- package/src/app/docs/guides/page.tsx +0 -67
- package/src/app/docs/layout.tsx +0 -15
- package/src/app/docs/metadata.ts +0 -31
- package/src/app/docs/page.tsx +0 -572
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +0 -123
- package/src/app/layout.tsx +0 -37
- package/src/app/ndpr-demos/breach/page.tsx +0 -354
- package/src/app/ndpr-demos/consent/page.tsx +0 -366
- package/src/app/ndpr-demos/dpia/page.tsx +0 -495
- package/src/app/ndpr-demos/dsr/page.tsx +0 -280
- package/src/app/ndpr-demos/page.tsx +0 -73
- package/src/app/ndpr-demos/policy/page.tsx +0 -771
- package/src/app/page.tsx +0 -452
- package/src/components/ErrorBoundary.tsx +0 -90
- package/src/components/breach-notification/BreachNotificationForm.tsx +0 -479
- package/src/components/consent/ConsentBanner.tsx +0 -159
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +0 -419
- package/src/components/docs/DocLayout.tsx +0 -289
- package/src/components/docs/index.ts +0 -2
- package/src/components/dpia/DPIAQuestionnaire.tsx +0 -483
- package/src/components/privacy-policy/PolicyGenerator.tsx +0 -1062
- package/src/components/privacy-policy/data.ts +0 -98
- package/src/components/privacy-policy/shared/CheckboxField.tsx +0 -38
- package/src/components/privacy-policy/shared/CheckboxGroup.tsx +0 -85
- package/src/components/privacy-policy/shared/FormField.tsx +0 -79
- package/src/components/privacy-policy/shared/StepIndicator.tsx +0 -86
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +0 -335
- package/src/components/privacy-policy/steps/DataCollectionStep.tsx +0 -231
- package/src/components/privacy-policy/steps/DataSharingStep.tsx +0 -418
- package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +0 -202
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +0 -172
- package/src/components/ui/Badge.tsx +0 -46
- package/src/components/ui/Button.tsx +0 -59
- package/src/components/ui/Card.tsx +0 -92
- package/src/components/ui/Checkbox.tsx +0 -57
- package/src/components/ui/FormField.tsx +0 -50
- package/src/components/ui/Input.tsx +0 -38
- package/src/components/ui/Loading.tsx +0 -201
- package/src/components/ui/Select.tsx +0 -42
- package/src/components/ui/TextArea.tsx +0 -38
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/switch.tsx +0 -31
- package/src/components/ui/tabs.tsx +0 -66
- package/src/hooks/useConsent.ts +0 -64
- package/src/hooks/useLoadingState.ts +0 -85
- package/src/lib/consentService.ts +0 -137
- package/src/lib/dpiaQuestions.ts +0 -148
- package/src/lib/requestService.ts +0 -75
- package/src/lib/sanitize.ts +0 -108
- package/src/lib/storage.ts +0 -222
- package/src/lib/utils.ts +0 -6
- package/src/types/html-to-docx.d.ts +0 -30
- package/src/types/index.ts +0 -72
- package/tailwind.config.ts +0 -65
- package/tsconfig.json +0 -41
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
export const defaultDataPurposes = [
|
|
2
|
-
'Providing and maintaining our services',
|
|
3
|
-
'Processing transactions',
|
|
4
|
-
'Sending administrative information',
|
|
5
|
-
'Improving user experience',
|
|
6
|
-
'Marketing and promotional communications',
|
|
7
|
-
'Analytics and research',
|
|
8
|
-
'Legal compliance',
|
|
9
|
-
'Customer support',
|
|
10
|
-
'Personalization of content',
|
|
11
|
-
'Account management',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
export const defaultSecurityMeasures = [
|
|
15
|
-
'Encryption of sensitive data in transit and at rest',
|
|
16
|
-
'Multi-factor authentication for system access',
|
|
17
|
-
'Regular security assessments and penetration testing',
|
|
18
|
-
'Access controls with principle of least privilege',
|
|
19
|
-
'Data backup procedures with regular testing',
|
|
20
|
-
'Comprehensive staff training on data protection',
|
|
21
|
-
'Documented incident response plan with regular drills',
|
|
22
|
-
'Physical security measures including CCTV and access controls',
|
|
23
|
-
'Network security monitoring with intrusion detection',
|
|
24
|
-
'Vulnerability management and timely patching',
|
|
25
|
-
'Third-party security assessments for vendors',
|
|
26
|
-
'Data loss prevention (DLP) systems',
|
|
27
|
-
'Regular security audits and compliance checks',
|
|
28
|
-
'Secure development practices and code reviews',
|
|
29
|
-
'Privacy by design and default in all systems',
|
|
30
|
-
'Data minimization and pseudonymization where appropriate',
|
|
31
|
-
'Secure disposal of data and equipment',
|
|
32
|
-
'Endpoint protection and mobile device management',
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
export const dataSubjectCategories = [
|
|
36
|
-
'Customers and clients',
|
|
37
|
-
'Employees and contractors',
|
|
38
|
-
'Job applicants',
|
|
39
|
-
'Suppliers and vendors',
|
|
40
|
-
'Website visitors and users',
|
|
41
|
-
'Marketing recipients',
|
|
42
|
-
'Business partners',
|
|
43
|
-
'Shareholders and investors',
|
|
44
|
-
'Children (under 18 years)',
|
|
45
|
-
'Vulnerable individuals',
|
|
46
|
-
'Patients (for healthcare organizations)',
|
|
47
|
-
'Students (for educational institutions)',
|
|
48
|
-
'Members (for membership organizations)',
|
|
49
|
-
'Beneficiaries (for charities/NGOs)',
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
export const trackingTechnologies = [
|
|
53
|
-
'HTTP cookies',
|
|
54
|
-
'Web beacons and pixel tags',
|
|
55
|
-
'Local storage objects',
|
|
56
|
-
'Session replay tools',
|
|
57
|
-
'Device fingerprinting',
|
|
58
|
-
'IP address tracking',
|
|
59
|
-
'JavaScript trackers',
|
|
60
|
-
'Mobile app SDKs',
|
|
61
|
-
'Cross-device tracking',
|
|
62
|
-
'Social media widgets and buttons',
|
|
63
|
-
'Heatmaps and clicktracking',
|
|
64
|
-
'UTM parameters',
|
|
65
|
-
'Server logs',
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
export const transferSafeguards = [
|
|
69
|
-
'Standard Contractual Clauses (SCCs)',
|
|
70
|
-
'Binding Corporate Rules (BCRs)',
|
|
71
|
-
'Adequacy decisions by the Nigerian Data Protection Commission',
|
|
72
|
-
'Data Processing Agreements with appropriate safeguards',
|
|
73
|
-
'Explicit consent of the data subject',
|
|
74
|
-
'Necessary for the performance of a contract',
|
|
75
|
-
'Necessary for important reasons of public interest',
|
|
76
|
-
'Necessary to protect vital interests',
|
|
77
|
-
'Transfer is from a register intended to provide information to the public',
|
|
78
|
-
'Compliance with NDPR Section 2.11 and 2.12 on international transfers',
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
export const defaultCookieTypes = [
|
|
82
|
-
'Strictly necessary cookies',
|
|
83
|
-
'Functional cookies',
|
|
84
|
-
'Performance cookies',
|
|
85
|
-
'Targeting/advertising cookies',
|
|
86
|
-
'Social media cookies',
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
export const commonTransferCountries = [
|
|
90
|
-
'United States',
|
|
91
|
-
'United Kingdom',
|
|
92
|
-
'European Union countries',
|
|
93
|
-
'Canada',
|
|
94
|
-
'Australia',
|
|
95
|
-
'India',
|
|
96
|
-
'South Africa',
|
|
97
|
-
'Kenya',
|
|
98
|
-
];
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { Checkbox } from '@/components/ui/Checkbox';
|
|
5
|
-
import { cn } from '@/lib/utils';
|
|
6
|
-
|
|
7
|
-
interface CheckboxFieldProps {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string;
|
|
10
|
-
label: string;
|
|
11
|
-
checked: boolean;
|
|
12
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
13
|
-
className?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function CheckboxField({
|
|
18
|
-
id,
|
|
19
|
-
name,
|
|
20
|
-
label,
|
|
21
|
-
checked,
|
|
22
|
-
onChange,
|
|
23
|
-
className = '',
|
|
24
|
-
description,
|
|
25
|
-
}: CheckboxFieldProps) {
|
|
26
|
-
return (
|
|
27
|
-
<div className={cn('mb-4', className)}>
|
|
28
|
-
<Checkbox
|
|
29
|
-
id={id}
|
|
30
|
-
name={name}
|
|
31
|
-
label={label}
|
|
32
|
-
checked={checked}
|
|
33
|
-
onChange={onChange}
|
|
34
|
-
description={description}
|
|
35
|
-
/>
|
|
36
|
-
</div>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { cn } from '@/lib/utils';
|
|
5
|
-
|
|
6
|
-
interface CheckboxGroupProps {
|
|
7
|
-
title: string;
|
|
8
|
-
items: string[];
|
|
9
|
-
selectedItems: string[];
|
|
10
|
-
onToggleItem: (item: string) => void;
|
|
11
|
-
required?: boolean;
|
|
12
|
-
error?: string;
|
|
13
|
-
columns?: 1 | 2 | 3;
|
|
14
|
-
className?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function CheckboxGroup({
|
|
18
|
-
title,
|
|
19
|
-
items,
|
|
20
|
-
selectedItems,
|
|
21
|
-
onToggleItem,
|
|
22
|
-
required = false,
|
|
23
|
-
error,
|
|
24
|
-
columns = 2,
|
|
25
|
-
className = '',
|
|
26
|
-
}: CheckboxGroupProps) {
|
|
27
|
-
return (
|
|
28
|
-
<div className={cn("relative", className)}>
|
|
29
|
-
{title && (
|
|
30
|
-
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
31
|
-
{title} {required && <span className="text-red-500">*</span>}
|
|
32
|
-
</label>
|
|
33
|
-
)}
|
|
34
|
-
|
|
35
|
-
{error && (
|
|
36
|
-
<p className="mt-1 mb-2 text-sm font-medium text-red-600 dark:text-red-400">{error}</p>
|
|
37
|
-
)}
|
|
38
|
-
|
|
39
|
-
<div className={cn(
|
|
40
|
-
"grid gap-x-4 gap-y-2 mt-1",
|
|
41
|
-
columns === 1 ? "grid-cols-1" :
|
|
42
|
-
columns === 2 ? "grid-cols-1 sm:grid-cols-2" :
|
|
43
|
-
"grid-cols-1 sm:grid-cols-2 md:grid-cols-3"
|
|
44
|
-
)}>
|
|
45
|
-
{items.map((item) => {
|
|
46
|
-
const isSelected = selectedItems.includes(item);
|
|
47
|
-
const itemId = `item-${item.replace(/\s+/g, '-').toLowerCase()}`;
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div key={itemId} className="flex items-start group">
|
|
51
|
-
<div className="flex items-center h-5">
|
|
52
|
-
<input
|
|
53
|
-
id={itemId}
|
|
54
|
-
type="checkbox"
|
|
55
|
-
checked={isSelected}
|
|
56
|
-
onChange={() => onToggleItem(item)}
|
|
57
|
-
className={cn(
|
|
58
|
-
"h-4 w-4 rounded border-2 transition-colors duration-200",
|
|
59
|
-
"focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
|
|
60
|
-
isSelected
|
|
61
|
-
? "bg-blue-600 border-blue-600 dark:bg-blue-500 dark:border-blue-500"
|
|
62
|
-
: "border-gray-300 dark:border-gray-600",
|
|
63
|
-
"hover:border-blue-500 dark:hover:border-blue-400"
|
|
64
|
-
)}
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
<label
|
|
68
|
-
htmlFor={itemId}
|
|
69
|
-
className={cn(
|
|
70
|
-
"ml-2 text-sm leading-tight",
|
|
71
|
-
isSelected
|
|
72
|
-
? "text-gray-900 dark:text-white font-medium"
|
|
73
|
-
: "text-gray-700 dark:text-gray-300",
|
|
74
|
-
"group-hover:text-gray-900 dark:group-hover:text-white transition-colors duration-200"
|
|
75
|
-
)}
|
|
76
|
-
>
|
|
77
|
-
{item}
|
|
78
|
-
</label>
|
|
79
|
-
</div>
|
|
80
|
-
);
|
|
81
|
-
})}
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { cn } from '@/lib/utils';
|
|
5
|
-
|
|
6
|
-
interface FormFieldProps {
|
|
7
|
-
id: string;
|
|
8
|
-
label: string;
|
|
9
|
-
required?: boolean;
|
|
10
|
-
error?: string;
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
className?: string;
|
|
13
|
-
description?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function FormField({
|
|
17
|
-
id,
|
|
18
|
-
label,
|
|
19
|
-
required = false,
|
|
20
|
-
error,
|
|
21
|
-
children,
|
|
22
|
-
className = '',
|
|
23
|
-
description,
|
|
24
|
-
}: FormFieldProps) {
|
|
25
|
-
const hasError = !!error;
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<div className={cn('mb-5 space-y-2', className)}>
|
|
29
|
-
<div className="flex items-center justify-between">
|
|
30
|
-
<label
|
|
31
|
-
htmlFor={id}
|
|
32
|
-
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-gray-700 dark:text-gray-200"
|
|
33
|
-
>
|
|
34
|
-
{label} {required && <span className="text-red-500 ml-1" aria-hidden="true">*</span>}
|
|
35
|
-
{required && <span className="sr-only">(required)</span>}
|
|
36
|
-
</label>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
{description && (
|
|
40
|
-
<p className="text-sm text-gray-500 dark:text-gray-400">{description}</p>
|
|
41
|
-
)}
|
|
42
|
-
|
|
43
|
-
<div className={`relative ${hasError ? 'has-error' : ''}`}>
|
|
44
|
-
{children}
|
|
45
|
-
|
|
46
|
-
{hasError && (
|
|
47
|
-
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
48
|
-
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-red-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
49
|
-
<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" />
|
|
50
|
-
</svg>
|
|
51
|
-
</div>
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
{hasError && (
|
|
56
|
-
<p className="text-sm font-medium text-red-600 dark:text-red-400" id={`${id}-error`} role="alert">
|
|
57
|
-
{error}
|
|
58
|
-
</p>
|
|
59
|
-
)}
|
|
60
|
-
|
|
61
|
-
<style jsx>{`
|
|
62
|
-
/* This ensures the error icon doesn't appear on elements that shouldn't have it */
|
|
63
|
-
.has-error > :global(textarea),
|
|
64
|
-
.has-error > :global(input:not([type="checkbox"]):not([type="radio"])),
|
|
65
|
-
.has-error > :global(select) {
|
|
66
|
-
padding-right: 2.5rem;
|
|
67
|
-
border-color: rgb(239, 68, 68);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.has-error > :global(textarea:focus),
|
|
71
|
-
.has-error > :global(input:focus),
|
|
72
|
-
.has-error > :global(select:focus) {
|
|
73
|
-
border-color: rgb(239, 68, 68);
|
|
74
|
-
--tw-ring-color: rgba(239, 68, 68, 0.2);
|
|
75
|
-
}
|
|
76
|
-
`}</style>
|
|
77
|
-
</div>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
|
|
5
|
-
interface StepIndicatorProps {
|
|
6
|
-
currentStep: number;
|
|
7
|
-
totalSteps: number;
|
|
8
|
-
stepLabels: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function StepIndicator({
|
|
12
|
-
currentStep,
|
|
13
|
-
totalSteps,
|
|
14
|
-
stepLabels
|
|
15
|
-
}: StepIndicatorProps) {
|
|
16
|
-
return (
|
|
17
|
-
<div className="mb-10" role="navigation" aria-label="Form progress">
|
|
18
|
-
<div className="flex items-center justify-between">
|
|
19
|
-
{Array.from({ length: totalSteps }).map((_, index) => {
|
|
20
|
-
const stepNumber = index + 1;
|
|
21
|
-
const isActive = stepNumber === currentStep;
|
|
22
|
-
const isCompleted = stepNumber < currentStep;
|
|
23
|
-
const isPending = stepNumber > currentStep;
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div
|
|
27
|
-
key={stepNumber}
|
|
28
|
-
className={`flex items-center ${stepNumber < totalSteps ? 'w-full' : ''}`}
|
|
29
|
-
>
|
|
30
|
-
<div
|
|
31
|
-
className={`
|
|
32
|
-
flex items-center justify-center w-10 h-10 rounded-full text-sm font-medium
|
|
33
|
-
transition-all duration-200 ease-in-out
|
|
34
|
-
${isActive ? 'bg-indigo-600 text-white ring-4 ring-indigo-100 dark:ring-indigo-900/30 scale-110' : ''}
|
|
35
|
-
${isCompleted ? 'bg-indigo-600 text-white' : ''}
|
|
36
|
-
${isPending ? 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400' : ''}
|
|
37
|
-
`}
|
|
38
|
-
aria-current={isActive ? 'step' : undefined}
|
|
39
|
-
aria-label={`Step ${stepNumber}: ${stepLabels[index]}`}
|
|
40
|
-
>
|
|
41
|
-
{isCompleted ? (
|
|
42
|
-
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
43
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
44
|
-
</svg>
|
|
45
|
-
) : (
|
|
46
|
-
stepNumber
|
|
47
|
-
)}
|
|
48
|
-
</div>
|
|
49
|
-
{stepNumber < totalSteps && (
|
|
50
|
-
<div
|
|
51
|
-
className={`
|
|
52
|
-
flex-1 h-1 mx-2 rounded-full
|
|
53
|
-
transition-all duration-200 ease-in-out
|
|
54
|
-
${stepNumber < currentStep ? 'bg-indigo-600' : 'bg-gray-200 dark:bg-gray-700'}
|
|
55
|
-
`}
|
|
56
|
-
aria-hidden="true"
|
|
57
|
-
/>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
})}
|
|
62
|
-
</div>
|
|
63
|
-
<div className="flex justify-between mt-3">
|
|
64
|
-
{stepLabels.map((label, index) => {
|
|
65
|
-
const stepNumber = index + 1;
|
|
66
|
-
const isActive = stepNumber === currentStep;
|
|
67
|
-
const isCompleted = stepNumber < currentStep;
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<span
|
|
71
|
-
key={index}
|
|
72
|
-
className={`
|
|
73
|
-
text-sm transition-colors duration-200 ease-in-out
|
|
74
|
-
${isActive ? 'text-indigo-600 dark:text-indigo-400 font-medium' : ''}
|
|
75
|
-
${isCompleted ? 'text-gray-700 dark:text-gray-300' : ''}
|
|
76
|
-
${!isActive && !isCompleted ? 'text-gray-500 dark:text-gray-400' : ''}
|
|
77
|
-
`}
|
|
78
|
-
>
|
|
79
|
-
{label}
|
|
80
|
-
</span>
|
|
81
|
-
);
|
|
82
|
-
})}
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
}
|