@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.
- package/.claude/settings.local.json +20 -0
- package/.eslintrc.json +10 -0
- package/.github/workflows/ci.yml +36 -0
- package/.github/workflows/nextjs.yml +104 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc.js +4 -0
- package/.nvmrc +1 -0
- package/.versionrc +17 -0
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +90 -0
- package/CNAME +1 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +84 -447
- package/RELEASE-NOTES-v1.0.0.md +140 -0
- package/RELEASE-NOTES-v1.0.1.md +69 -0
- package/SECURITY.md +21 -0
- package/commitlint.config.js +36 -0
- package/components.json +21 -0
- package/eslint.config.mjs +16 -0
- package/jest.config.js +31 -0
- package/jest.setup.js +15 -0
- package/next.config.js +15 -0
- package/next.config.ts +62 -0
- package/package.json +70 -52
- package/packages/ndpr-toolkit/README.md +467 -0
- package/packages/ndpr-toolkit/jest.config.js +23 -0
- package/packages/ndpr-toolkit/package-lock.json +8197 -0
- package/packages/ndpr-toolkit/package.json +71 -0
- package/packages/ndpr-toolkit/rollup.config.js +34 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentBanner.test.tsx +119 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentManager.test.tsx +122 -0
- package/packages/ndpr-toolkit/src/__tests__/components/consent/ConsentStorage.test.tsx +270 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRDashboard.test.tsx +199 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRRequestForm.test.tsx +224 -0
- package/packages/ndpr-toolkit/src/__tests__/components/dsr/DSRTracker.test.tsx +104 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useConsent.test.tsx +161 -0
- package/packages/ndpr-toolkit/src/__tests__/hooks/useDSR.test.tsx +330 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/breach.test.ts +149 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/consent.test.ts +88 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dpia.test.ts +160 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/dsr.test.ts +110 -0
- package/packages/ndpr-toolkit/src/__tests__/utils/privacy.test.ts +97 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachNotificationManager.tsx +701 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachReportForm.tsx +631 -0
- package/packages/ndpr-toolkit/src/components/breach/BreachRiskAssessment.tsx +569 -0
- package/packages/ndpr-toolkit/src/components/breach/RegulatoryReportGenerator.tsx +496 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentBanner.tsx +270 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentManager.tsx +217 -0
- package/packages/ndpr-toolkit/src/components/consent/ConsentStorage.tsx +206 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAQuestionnaire.tsx +342 -0
- package/packages/ndpr-toolkit/src/components/dpia/DPIAReport.tsx +373 -0
- package/packages/ndpr-toolkit/src/components/dpia/StepIndicator.tsx +174 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRDashboard.tsx +717 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRRequestForm.tsx +476 -0
- package/packages/ndpr-toolkit/src/components/dsr/DSRTracker.tsx +620 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyExporter.tsx +541 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyGenerator.tsx +454 -0
- package/packages/ndpr-toolkit/src/components/policy/PolicyPreview.tsx +333 -0
- package/packages/ndpr-toolkit/src/hooks/useBreach.ts +409 -0
- package/packages/ndpr-toolkit/src/hooks/useConsent.ts +263 -0
- package/packages/ndpr-toolkit/src/hooks/useDPIA.ts +457 -0
- package/packages/ndpr-toolkit/src/hooks/useDSR.ts +236 -0
- package/packages/ndpr-toolkit/src/hooks/usePrivacyPolicy.ts +428 -0
- package/{dist/index.d.ts → packages/ndpr-toolkit/src/index.ts} +13 -0
- package/packages/ndpr-toolkit/src/setupTests.ts +5 -0
- package/packages/ndpr-toolkit/src/types/breach.ts +283 -0
- package/packages/ndpr-toolkit/src/types/consent.ts +111 -0
- package/packages/ndpr-toolkit/src/types/dpia.ts +236 -0
- package/packages/ndpr-toolkit/src/types/dsr.ts +192 -0
- package/packages/ndpr-toolkit/src/types/index.ts +42 -0
- package/packages/ndpr-toolkit/src/types/privacy.ts +246 -0
- package/packages/ndpr-toolkit/src/utils/breach.ts +122 -0
- package/packages/ndpr-toolkit/src/utils/consent.ts +51 -0
- package/packages/ndpr-toolkit/src/utils/dpia.ts +104 -0
- package/packages/ndpr-toolkit/src/utils/dsr.ts +77 -0
- package/packages/ndpr-toolkit/src/utils/privacy.ts +100 -0
- package/packages/ndpr-toolkit/tsconfig.json +23 -0
- package/postcss.config.mjs +5 -0
- package/public/NDPR TOOLKIT.svg +1 -0
- package/public/favicon/android-chrome-192x192.png +0 -0
- package/public/favicon/android-chrome-512x512.png +0 -0
- package/public/favicon/apple-touch-icon.png +0 -0
- package/public/favicon/favicon-16x16.png +0 -0
- package/public/favicon/favicon-32x32.png +0 -0
- package/public/favicon/site.webmanifest +1 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/ndpr-toolkit-logo.svg +108 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/__tests__/example.test.ts +13 -0
- package/src/__tests__/requestService.test.ts +57 -0
- package/src/app/accessibility.css +70 -0
- package/src/app/docs/components/DocLayout.tsx +267 -0
- package/src/app/docs/components/breach-notification/page.tsx +797 -0
- package/src/app/docs/components/consent-management/page.tsx +576 -0
- package/src/app/docs/components/data-subject-rights/page.tsx +511 -0
- package/src/app/docs/components/dpia-questionnaire/layout.tsx +15 -0
- package/src/app/docs/components/dpia-questionnaire/metadata.ts +31 -0
- package/src/app/docs/components/dpia-questionnaire/page.tsx +666 -0
- package/src/app/docs/components/hooks/page.tsx +305 -0
- package/src/app/docs/components/page.tsx +84 -0
- package/src/app/docs/components/privacy-policy-generator/page.tsx +634 -0
- package/src/app/docs/guides/breach-notification-process/components/BestPractices.tsx +123 -0
- package/src/app/docs/guides/breach-notification-process/components/ImplementationSteps.tsx +328 -0
- package/src/app/docs/guides/breach-notification-process/components/Introduction.tsx +28 -0
- package/src/app/docs/guides/breach-notification-process/components/NotificationTimeline.tsx +91 -0
- package/src/app/docs/guides/breach-notification-process/components/Resources.tsx +118 -0
- package/src/app/docs/guides/breach-notification-process/page.tsx +39 -0
- package/src/app/docs/guides/conducting-dpia/page.tsx +593 -0
- package/src/app/docs/guides/data-subject-requests/page.tsx +666 -0
- package/src/app/docs/guides/managing-consent/page.tsx +738 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ComplianceChecklist.tsx +296 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/ImplementationTools.tsx +145 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Introduction.tsx +33 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/KeyRequirements.tsx +99 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/components/Resources.tsx +159 -0
- package/src/app/docs/guides/ndpr-compliance-checklist/page.tsx +38 -0
- package/src/app/docs/guides/page.tsx +67 -0
- package/src/app/docs/layout.tsx +15 -0
- package/src/app/docs/metadata.ts +31 -0
- package/src/app/docs/page.tsx +572 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +123 -0
- package/src/app/layout.tsx +37 -0
- package/src/app/ndpr-demos/breach/page.tsx +354 -0
- package/src/app/ndpr-demos/consent/page.tsx +366 -0
- package/src/app/ndpr-demos/dpia/page.tsx +495 -0
- package/src/app/ndpr-demos/dsr/page.tsx +280 -0
- package/src/app/ndpr-demos/page.tsx +73 -0
- package/src/app/ndpr-demos/policy/page.tsx +771 -0
- package/src/app/page.tsx +452 -0
- package/src/components/ErrorBoundary.tsx +90 -0
- package/src/components/breach-notification/BreachNotificationForm.tsx +479 -0
- package/src/components/consent/ConsentBanner.tsx +159 -0
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +419 -0
- package/src/components/docs/DocLayout.tsx +289 -0
- package/src/components/docs/index.ts +2 -0
- package/src/components/dpia/DPIAQuestionnaire.tsx +483 -0
- package/src/components/privacy-policy/PolicyGenerator.tsx +1062 -0
- package/src/components/privacy-policy/data.ts +98 -0
- package/src/components/privacy-policy/shared/CheckboxField.tsx +38 -0
- package/src/components/privacy-policy/shared/CheckboxGroup.tsx +85 -0
- package/src/components/privacy-policy/shared/FormField.tsx +79 -0
- package/src/components/privacy-policy/shared/StepIndicator.tsx +86 -0
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +335 -0
- package/src/components/privacy-policy/steps/DataCollectionStep.tsx +231 -0
- package/src/components/privacy-policy/steps/DataSharingStep.tsx +418 -0
- package/src/components/privacy-policy/steps/OrganizationInfoStep.tsx +202 -0
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +172 -0
- package/src/components/ui/Badge.tsx +46 -0
- package/src/components/ui/Button.tsx +59 -0
- package/src/components/ui/Card.tsx +92 -0
- package/src/components/ui/Checkbox.tsx +57 -0
- package/src/components/ui/FormField.tsx +50 -0
- package/src/components/ui/Input.tsx +38 -0
- package/src/components/ui/Loading.tsx +201 -0
- package/src/components/ui/Select.tsx +42 -0
- package/src/components/ui/TextArea.tsx +38 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/hooks/useConsent.ts +64 -0
- package/src/hooks/useLoadingState.ts +85 -0
- package/src/lib/consentService.ts +137 -0
- package/src/lib/dpiaQuestions.ts +148 -0
- package/src/lib/requestService.ts +75 -0
- package/src/lib/sanitize.ts +108 -0
- package/src/lib/storage.ts +222 -0
- package/src/lib/utils.ts +6 -0
- package/src/types/html-to-docx.d.ts +30 -0
- package/src/types/index.ts +72 -0
- package/tailwind.config.ts +65 -0
- package/tsconfig.json +41 -0
- package/dist/components/breach/BreachNotificationManager.d.ts +0 -62
- package/dist/components/breach/BreachReportForm.d.ts +0 -66
- package/dist/components/breach/BreachRiskAssessment.d.ts +0 -50
- package/dist/components/breach/RegulatoryReportGenerator.d.ts +0 -94
- package/dist/components/consent/ConsentBanner.d.ts +0 -79
- package/dist/components/consent/ConsentManager.d.ts +0 -73
- package/dist/components/consent/ConsentStorage.d.ts +0 -41
- package/dist/components/dpia/DPIAQuestionnaire.d.ts +0 -70
- package/dist/components/dpia/DPIAReport.d.ts +0 -40
- package/dist/components/dpia/StepIndicator.d.ts +0 -64
- package/dist/components/dsr/DSRDashboard.d.ts +0 -58
- package/dist/components/dsr/DSRRequestForm.d.ts +0 -74
- package/dist/components/dsr/DSRTracker.d.ts +0 -56
- package/dist/components/policy/PolicyExporter.d.ts +0 -65
- package/dist/components/policy/PolicyGenerator.d.ts +0 -54
- package/dist/components/policy/PolicyPreview.d.ts +0 -71
- package/dist/hooks/useBreach.d.ts +0 -97
- package/dist/hooks/useConsent.d.ts +0 -63
- package/dist/hooks/useDPIA.d.ts +0 -92
- package/dist/hooks/useDSR.d.ts +0 -72
- package/dist/hooks/usePrivacyPolicy.d.ts +0 -87
- package/dist/index.esm.js +0 -2
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/setupTests.d.ts +0 -2
- package/dist/types/breach.d.ts +0 -239
- package/dist/types/consent.d.ts +0 -95
- package/dist/types/dpia.d.ts +0 -196
- package/dist/types/dsr.d.ts +0 -162
- package/dist/types/privacy.d.ts +0 -204
- package/dist/utils/breach.d.ts +0 -14
- package/dist/utils/consent.d.ts +0 -10
- package/dist/utils/dpia.d.ts +0 -12
- package/dist/utils/dsr.d.ts +0 -11
- package/dist/utils/privacy.d.ts +0 -12
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
@import "./accessibility.css";
|
|
4
|
+
|
|
5
|
+
@custom-variant dark (&:is(.dark *));
|
|
6
|
+
|
|
7
|
+
@theme inline {
|
|
8
|
+
--color-background: var(--background);
|
|
9
|
+
--color-foreground: var(--foreground);
|
|
10
|
+
--font-sans: var(--font-geist-sans);
|
|
11
|
+
--font-mono: var(--font-geist-mono);
|
|
12
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
13
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
14
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
15
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
16
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
17
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
18
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
19
|
+
--color-sidebar: var(--sidebar);
|
|
20
|
+
--color-chart-5: var(--chart-5);
|
|
21
|
+
--color-chart-4: var(--chart-4);
|
|
22
|
+
--color-chart-3: var(--chart-3);
|
|
23
|
+
--color-chart-2: var(--chart-2);
|
|
24
|
+
--color-chart-1: var(--chart-1);
|
|
25
|
+
--color-ring: var(--ring);
|
|
26
|
+
--color-input: var(--input);
|
|
27
|
+
--color-border: var(--border);
|
|
28
|
+
--color-destructive: var(--destructive);
|
|
29
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
30
|
+
--color-accent: var(--accent);
|
|
31
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
32
|
+
--color-muted: var(--muted);
|
|
33
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
34
|
+
--color-secondary: var(--secondary);
|
|
35
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
36
|
+
--color-primary: var(--primary);
|
|
37
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
38
|
+
--color-popover: var(--popover);
|
|
39
|
+
--color-card-foreground: var(--card-foreground);
|
|
40
|
+
--color-card: var(--card);
|
|
41
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
42
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
43
|
+
--radius-lg: var(--radius);
|
|
44
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
:root {
|
|
48
|
+
--radius: 0.625rem;
|
|
49
|
+
--background: oklch(1 0 0);
|
|
50
|
+
--foreground: oklch(0.13 0.028 261.692);
|
|
51
|
+
--card: oklch(1 0 0);
|
|
52
|
+
--card-foreground: oklch(0.13 0.028 261.692);
|
|
53
|
+
--popover: oklch(1 0 0);
|
|
54
|
+
--popover-foreground: oklch(0.13 0.028 261.692);
|
|
55
|
+
--primary: oklch(0.21 0.034 264.665);
|
|
56
|
+
--primary-foreground: oklch(0.985 0.002 247.839);
|
|
57
|
+
--secondary: oklch(0.967 0.003 264.542);
|
|
58
|
+
--secondary-foreground: oklch(0.21 0.034 264.665);
|
|
59
|
+
--muted: oklch(0.967 0.003 264.542);
|
|
60
|
+
--muted-foreground: oklch(0.551 0.027 264.364);
|
|
61
|
+
--accent: oklch(0.967 0.003 264.542);
|
|
62
|
+
--accent-foreground: oklch(0.21 0.034 264.665);
|
|
63
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
64
|
+
--border: oklch(0.928 0.006 264.531);
|
|
65
|
+
--input: oklch(0.928 0.006 264.531);
|
|
66
|
+
--ring: oklch(0.707 0.022 261.325);
|
|
67
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
68
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
69
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
70
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
71
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
72
|
+
--sidebar: oklch(0.985 0.002 247.839);
|
|
73
|
+
--sidebar-foreground: oklch(0.13 0.028 261.692);
|
|
74
|
+
--sidebar-primary: oklch(0.21 0.034 264.665);
|
|
75
|
+
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
|
|
76
|
+
--sidebar-accent: oklch(0.967 0.003 264.542);
|
|
77
|
+
--sidebar-accent-foreground: oklch(0.21 0.034 264.665);
|
|
78
|
+
--sidebar-border: oklch(0.928 0.006 264.531);
|
|
79
|
+
--sidebar-ring: oklch(0.707 0.022 261.325);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.dark {
|
|
83
|
+
--background: oklch(0.13 0.028 261.692);
|
|
84
|
+
--foreground: oklch(0.985 0.002 247.839);
|
|
85
|
+
--card: oklch(0.21 0.034 264.665);
|
|
86
|
+
--card-foreground: oklch(0.985 0.002 247.839);
|
|
87
|
+
--popover: oklch(0.21 0.034 264.665);
|
|
88
|
+
--popover-foreground: oklch(0.985 0.002 247.839);
|
|
89
|
+
--primary: oklch(0.928 0.006 264.531);
|
|
90
|
+
--primary-foreground: oklch(0.21 0.034 264.665);
|
|
91
|
+
--secondary: oklch(0.278 0.033 256.848);
|
|
92
|
+
--secondary-foreground: oklch(0.985 0.002 247.839);
|
|
93
|
+
--muted: oklch(0.278 0.033 256.848);
|
|
94
|
+
--muted-foreground: oklch(0.707 0.022 261.325);
|
|
95
|
+
--accent: oklch(0.278 0.033 256.848);
|
|
96
|
+
--accent-foreground: oklch(0.985 0.002 247.839);
|
|
97
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
98
|
+
--border: oklch(1 0 0 / 10%);
|
|
99
|
+
--input: oklch(1 0 0 / 15%);
|
|
100
|
+
--ring: oklch(0.551 0.027 264.364);
|
|
101
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
102
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
103
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
104
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
105
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
106
|
+
--sidebar: oklch(0.21 0.034 264.665);
|
|
107
|
+
--sidebar-foreground: oklch(0.985 0.002 247.839);
|
|
108
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
109
|
+
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
|
|
110
|
+
--sidebar-accent: oklch(0.278 0.033 256.848);
|
|
111
|
+
--sidebar-accent-foreground: oklch(0.985 0.002 247.839);
|
|
112
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
113
|
+
--sidebar-ring: oklch(0.551 0.027 264.364);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@layer base {
|
|
117
|
+
* {
|
|
118
|
+
@apply border-border outline-ring/50;
|
|
119
|
+
}
|
|
120
|
+
body {
|
|
121
|
+
@apply bg-background text-foreground;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import { ErrorBoundary } from "@/components/ErrorBoundary";
|
|
4
|
+
import "./globals.css";
|
|
5
|
+
|
|
6
|
+
const geistSans = Geist({
|
|
7
|
+
variable: "--font-geist-sans",
|
|
8
|
+
subsets: ["latin"],
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const geistMono = Geist_Mono({
|
|
12
|
+
variable: "--font-geist-mono",
|
|
13
|
+
subsets: ["latin"],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const metadata: Metadata = {
|
|
17
|
+
title: "NDPR Toolkit - Nigerian Data Protection Compliance",
|
|
18
|
+
description: "Open-source toolkit for implementing NDPR and DPA compliant features in Nigerian applications",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function RootLayout({
|
|
22
|
+
children,
|
|
23
|
+
}: Readonly<{
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
}>) {
|
|
26
|
+
return (
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<body
|
|
29
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
30
|
+
>
|
|
31
|
+
<ErrorBoundary>
|
|
32
|
+
{children}
|
|
33
|
+
</ErrorBoundary>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
6
|
+
import { BreachReportForm, BreachRiskAssessment, BreachNotificationManager, BreachReport, RiskAssessment, RegulatoryReportGenerator } from '@tantainnovative/ndpr-toolkit';
|
|
7
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/Card';
|
|
9
|
+
|
|
10
|
+
export default function BreachDemoPage() {
|
|
11
|
+
const [activeTab, setActiveTab] = useState('notification');
|
|
12
|
+
const [isClient, setIsClient] = useState(false);
|
|
13
|
+
const [breaches, setBreaches] = useState<BreachReport[]>([]);
|
|
14
|
+
const [riskAssessments, setRiskAssessments] = useState<RiskAssessment[]>([]);
|
|
15
|
+
// Define interface based on documentation for regulatory notifications
|
|
16
|
+
interface RegulatoryNotification {
|
|
17
|
+
id: string;
|
|
18
|
+
breachId: string;
|
|
19
|
+
sentAt: number;
|
|
20
|
+
method: 'email' | 'portal' | 'letter' | 'other';
|
|
21
|
+
referenceNumber?: string;
|
|
22
|
+
content: string;
|
|
23
|
+
attachments?: Array<{
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
url: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const [regulatoryNotifications, setRegulatoryNotifications] = useState<RegulatoryNotification[]>([]);
|
|
32
|
+
const [selectedBreach, setSelectedBreach] = useState<BreachReport | null>(null);
|
|
33
|
+
|
|
34
|
+
// This effect runs only on the client side after hydration
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setIsClient(true);
|
|
37
|
+
|
|
38
|
+
// Load sample data for demo purposes
|
|
39
|
+
const sampleBreaches: BreachReport[] = [
|
|
40
|
+
{
|
|
41
|
+
id: uuidv4(),
|
|
42
|
+
title: 'Customer Database Unauthorized Access',
|
|
43
|
+
description: 'Unauthorized access to customer database detected through suspicious login patterns',
|
|
44
|
+
category: 'unauthorized_access',
|
|
45
|
+
discoveredAt: Date.now() - 5 * 24 * 60 * 60 * 1000, // 5 days ago
|
|
46
|
+
reportedAt: Date.now() - 4 * 24 * 60 * 60 * 1000, // 4 days ago
|
|
47
|
+
reporter: {
|
|
48
|
+
name: 'John Smith',
|
|
49
|
+
email: 'john@example.com',
|
|
50
|
+
department: 'IT Security'
|
|
51
|
+
},
|
|
52
|
+
affectedSystems: ['customer_database', 'user_accounts'],
|
|
53
|
+
dataTypes: ['name', 'email', 'phone', 'address'],
|
|
54
|
+
estimatedAffectedSubjects: 1200,
|
|
55
|
+
status: 'ongoing',
|
|
56
|
+
initialActions: 'Access blocked, passwords reset, system isolated for investigation'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: uuidv4(),
|
|
60
|
+
title: 'Employee Email Phishing Incident',
|
|
61
|
+
description: 'Several employees clicked on phishing links potentially exposing credentials',
|
|
62
|
+
category: 'phishing',
|
|
63
|
+
discoveredAt: Date.now() - 10 * 24 * 60 * 60 * 1000, // 10 days ago
|
|
64
|
+
reportedAt: Date.now() - 9 * 24 * 60 * 60 * 1000, // 9 days ago
|
|
65
|
+
reporter: {
|
|
66
|
+
name: 'Jane Doe',
|
|
67
|
+
email: 'jane@example.com',
|
|
68
|
+
department: 'HR'
|
|
69
|
+
},
|
|
70
|
+
affectedSystems: ['email_system', 'employee_accounts'],
|
|
71
|
+
dataTypes: ['email credentials'],
|
|
72
|
+
estimatedAffectedSubjects: 0, // No confirmed data exposure
|
|
73
|
+
status: 'resolved',
|
|
74
|
+
initialActions: 'Password resets, email scanning, blocking of malicious domains'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: uuidv4(),
|
|
78
|
+
title: 'Lost Company Laptop',
|
|
79
|
+
description: 'Employee reported company laptop lost during business travel',
|
|
80
|
+
category: 'device_loss',
|
|
81
|
+
discoveredAt: Date.now() - 15 * 24 * 60 * 60 * 1000, // 15 days ago
|
|
82
|
+
reportedAt: Date.now() - 15 * 24 * 60 * 60 * 1000, // 15 days ago
|
|
83
|
+
reporter: {
|
|
84
|
+
name: 'Robert Johnson',
|
|
85
|
+
email: 'robert@example.com',
|
|
86
|
+
department: 'Sales'
|
|
87
|
+
},
|
|
88
|
+
affectedSystems: ['laptop_device', 'company_data'],
|
|
89
|
+
dataTypes: ['name', 'email', 'project data'],
|
|
90
|
+
estimatedAffectedSubjects: 50,
|
|
91
|
+
status: 'resolved',
|
|
92
|
+
initialActions: 'Remote wipe initiated, account passwords changed'
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
setBreaches(sampleBreaches);
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const handleSubmitBreach = (data: {
|
|
100
|
+
title: string;
|
|
101
|
+
description: string;
|
|
102
|
+
category?: string;
|
|
103
|
+
reporter?: {
|
|
104
|
+
name?: string;
|
|
105
|
+
email?: string;
|
|
106
|
+
department?: string;
|
|
107
|
+
};
|
|
108
|
+
affectedSystems?: string[];
|
|
109
|
+
dataTypes?: string[];
|
|
110
|
+
estimatedAffectedSubjects?: number;
|
|
111
|
+
initialActions?: string;
|
|
112
|
+
}) => {
|
|
113
|
+
const newBreach: BreachReport = {
|
|
114
|
+
id: uuidv4(),
|
|
115
|
+
title: data.title,
|
|
116
|
+
description: data.description,
|
|
117
|
+
category: data.category || 'other',
|
|
118
|
+
discoveredAt: Date.now(),
|
|
119
|
+
reportedAt: Date.now(),
|
|
120
|
+
reporter: {
|
|
121
|
+
name: data.reporter?.name || 'Anonymous',
|
|
122
|
+
email: data.reporter?.email || 'anonymous@example.com',
|
|
123
|
+
department: data.reporter?.department || 'Unknown'
|
|
124
|
+
},
|
|
125
|
+
affectedSystems: data.affectedSystems || [],
|
|
126
|
+
dataTypes: data.dataTypes || [],
|
|
127
|
+
estimatedAffectedSubjects: data.estimatedAffectedSubjects || 0,
|
|
128
|
+
status: 'ongoing',
|
|
129
|
+
initialActions: data.initialActions || ''
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
setBreaches((prev) => [newBreach, ...prev]);
|
|
133
|
+
setActiveTab('register');
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const handleUpdateBreach = (breachId: string, updates: Partial<BreachReport>) => {
|
|
137
|
+
setBreaches((prev) =>
|
|
138
|
+
prev.map((breach) => {
|
|
139
|
+
if (breach.id === breachId) {
|
|
140
|
+
return {
|
|
141
|
+
...breach,
|
|
142
|
+
...updates
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return breach;
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const handleSelectBreach = (breachId: string) => {
|
|
151
|
+
const breach = breaches.find((b) => b.id === breachId);
|
|
152
|
+
setSelectedBreach(breach || null);
|
|
153
|
+
setActiveTab('assessment');
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Add a function to handle regulatory notifications
|
|
157
|
+
const handleGenerateReport = (breachId: string) => {
|
|
158
|
+
if (!selectedBreach) return;
|
|
159
|
+
|
|
160
|
+
// Create a sample regulatory notification based on the breach
|
|
161
|
+
const newNotification: RegulatoryNotification = {
|
|
162
|
+
id: uuidv4(),
|
|
163
|
+
breachId: breachId,
|
|
164
|
+
sentAt: Date.now(),
|
|
165
|
+
method: 'email',
|
|
166
|
+
content: `Notification regarding breach: ${selectedBreach.title}`,
|
|
167
|
+
referenceNumber: `REF-${Date.now().toString().substring(0, 8)}`
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Add to notifications list
|
|
171
|
+
setRegulatoryNotifications(prev => [...prev, newNotification]);
|
|
172
|
+
|
|
173
|
+
// Update breach status
|
|
174
|
+
handleUpdateBreach(breachId, { status: 'contained' }); // Using a valid status from the BreachReport type
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (!isClient) {
|
|
178
|
+
return <div>Loading...</div>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<div className="container mx-auto py-10">
|
|
183
|
+
<div className="mb-6">
|
|
184
|
+
<Link href="/ndpr-demos" className="text-blue-600 hover:underline">
|
|
185
|
+
← Back to NDPR Demos
|
|
186
|
+
</Link>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<h1 className="text-3xl font-bold mb-8">Breach Management Demo</h1>
|
|
190
|
+
|
|
191
|
+
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
|
192
|
+
<TabsList className="mb-6">
|
|
193
|
+
<TabsTrigger value="notification">Breach Notification</TabsTrigger>
|
|
194
|
+
<TabsTrigger value="register">Breach Register</TabsTrigger>
|
|
195
|
+
<TabsTrigger value="assessment">Breach Assessment</TabsTrigger>
|
|
196
|
+
</TabsList>
|
|
197
|
+
|
|
198
|
+
<TabsContent value="notification" className="mt-6">
|
|
199
|
+
<Card>
|
|
200
|
+
<CardHeader>
|
|
201
|
+
<CardTitle>Breach Notification Form</CardTitle>
|
|
202
|
+
<CardDescription>
|
|
203
|
+
This form is used to report and document data breaches within your organization.
|
|
204
|
+
</CardDescription>
|
|
205
|
+
</CardHeader>
|
|
206
|
+
<CardContent>
|
|
207
|
+
<BreachReportForm
|
|
208
|
+
onSubmit={handleSubmitBreach}
|
|
209
|
+
title="Report a Data Breach"
|
|
210
|
+
categories={[
|
|
211
|
+
{ id: 'unauthorized_access', name: 'Unauthorized Access', description: 'Unauthorized access to systems or data', defaultSeverity: 'high' },
|
|
212
|
+
{ id: 'phishing', name: 'Phishing Attack', description: 'Phishing or social engineering attack', defaultSeverity: 'medium' },
|
|
213
|
+
{ id: 'device_loss', name: 'Device Loss/Theft', description: 'Loss or theft of device containing personal data', defaultSeverity: 'medium' },
|
|
214
|
+
{ id: 'malware', name: 'Malware/Ransomware', description: 'Malware or ransomware infection', defaultSeverity: 'high' },
|
|
215
|
+
{ id: 'other', name: 'Other', description: 'Other type of data breach', defaultSeverity: 'medium' }
|
|
216
|
+
]}
|
|
217
|
+
/>
|
|
218
|
+
</CardContent>
|
|
219
|
+
</Card>
|
|
220
|
+
</TabsContent>
|
|
221
|
+
|
|
222
|
+
<TabsContent value="register" className="mt-6">
|
|
223
|
+
<Card>
|
|
224
|
+
<CardHeader>
|
|
225
|
+
<CardTitle>Breach Register</CardTitle>
|
|
226
|
+
<CardDescription>
|
|
227
|
+
This component maintains a register of all data breaches and their current status.
|
|
228
|
+
</CardDescription>
|
|
229
|
+
</CardHeader>
|
|
230
|
+
<CardContent>
|
|
231
|
+
<BreachNotificationManager
|
|
232
|
+
breachReports={breaches}
|
|
233
|
+
riskAssessments={riskAssessments}
|
|
234
|
+
regulatoryNotifications={regulatoryNotifications}
|
|
235
|
+
onSelectBreach={handleSelectBreach}
|
|
236
|
+
onRequestAssessment={(breachId) => {
|
|
237
|
+
handleSelectBreach(breachId);
|
|
238
|
+
setActiveTab('assessment');
|
|
239
|
+
}}
|
|
240
|
+
onRequestNotification={(breachId: string) => {
|
|
241
|
+
handleSelectBreach(breachId);
|
|
242
|
+
// Generate a regulatory notification
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
if (selectedBreach) {
|
|
245
|
+
handleGenerateReport(breachId);
|
|
246
|
+
}
|
|
247
|
+
}, 500);
|
|
248
|
+
}}
|
|
249
|
+
title="Data Breach Register"
|
|
250
|
+
description="A record of all data breaches and security incidents affecting personal data."
|
|
251
|
+
showBreachDetails={true}
|
|
252
|
+
showNotificationTimeline={true}
|
|
253
|
+
showDeadlineAlerts={true}
|
|
254
|
+
/>
|
|
255
|
+
</CardContent>
|
|
256
|
+
</Card>
|
|
257
|
+
</TabsContent>
|
|
258
|
+
|
|
259
|
+
<TabsContent value="assessment" className="mt-6">
|
|
260
|
+
<Card>
|
|
261
|
+
<CardHeader>
|
|
262
|
+
<CardTitle>Breach Assessment</CardTitle>
|
|
263
|
+
<CardDescription>
|
|
264
|
+
This component helps assess the severity and impact of a data breach.
|
|
265
|
+
</CardDescription>
|
|
266
|
+
</CardHeader>
|
|
267
|
+
<CardContent>
|
|
268
|
+
{selectedBreach ? (
|
|
269
|
+
<div className="space-y-8">
|
|
270
|
+
<BreachRiskAssessment
|
|
271
|
+
breachData={selectedBreach}
|
|
272
|
+
onComplete={(assessment: RiskAssessment) => {
|
|
273
|
+
if (selectedBreach) {
|
|
274
|
+
// Update the breach status based on the assessment
|
|
275
|
+
handleUpdateBreach(selectedBreach.id, {
|
|
276
|
+
status: assessment.riskLevel === 'critical' ? 'ongoing' : 'contained',
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Store the risk assessment
|
|
280
|
+
const newAssessment = {
|
|
281
|
+
...assessment,
|
|
282
|
+
id: uuidv4(),
|
|
283
|
+
breachId: selectedBreach.id,
|
|
284
|
+
assessedAt: Date.now(),
|
|
285
|
+
assessor: {
|
|
286
|
+
name: 'Demo User',
|
|
287
|
+
role: 'Data Protection Officer',
|
|
288
|
+
email: 'dpo@example.com'
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
setRiskAssessments(prev => {
|
|
293
|
+
// Remove any existing assessment for this breach
|
|
294
|
+
const filtered = prev.filter(a => a.breachId !== selectedBreach.id);
|
|
295
|
+
// Add the new assessment
|
|
296
|
+
return [...filtered, newAssessment];
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}}
|
|
300
|
+
title="Breach Risk Assessment"
|
|
301
|
+
description="Assess the severity, impact, and required actions for this data breach."
|
|
302
|
+
className=""
|
|
303
|
+
buttonClassName=""
|
|
304
|
+
showBreachSummary={true}
|
|
305
|
+
showNotificationRequirements={true}
|
|
306
|
+
/>
|
|
307
|
+
|
|
308
|
+
{/* Add Regulatory Report Generator component */}
|
|
309
|
+
<div className="mt-8 pt-8 border-t border-gray-200">
|
|
310
|
+
<h3 className="text-lg font-medium mb-4">Generate Regulatory Report</h3>
|
|
311
|
+
<RegulatoryReportGenerator
|
|
312
|
+
breachData={selectedBreach}
|
|
313
|
+
organizationInfo={{
|
|
314
|
+
name: 'Example Company Ltd.',
|
|
315
|
+
address: '123 Main Street, Lagos, Nigeria',
|
|
316
|
+
dpoName: 'John Doe',
|
|
317
|
+
dpoEmail: 'dpo@example.com',
|
|
318
|
+
dpoPhone: '+234 123 456 7890'
|
|
319
|
+
}}
|
|
320
|
+
onGenerate={() => handleGenerateReport(selectedBreach.id)}
|
|
321
|
+
/>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
) : (
|
|
325
|
+
<div className="p-4 text-center">
|
|
326
|
+
<p>No breach selected. Please select a breach from the Breach Register.</p>
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
329
|
+
</CardContent>
|
|
330
|
+
</Card>
|
|
331
|
+
</TabsContent>
|
|
332
|
+
</Tabs>
|
|
333
|
+
|
|
334
|
+
<div className="mt-10 p-4 bg-gray-100 rounded-lg">
|
|
335
|
+
<h2 className="text-xl font-semibold mb-2">Implementation Notes</h2>
|
|
336
|
+
<p className="mb-4">
|
|
337
|
+
This demo showcases the breach management components from the NDPR Toolkit:
|
|
338
|
+
</p>
|
|
339
|
+
<ul className="list-disc pl-5 space-y-2">
|
|
340
|
+
<li><code>BreachReportForm</code>: For reporting and documenting data breaches</li>
|
|
341
|
+
<li><code>BreachRegister</code>: For maintaining a record of all data breaches</li>
|
|
342
|
+
<li><code>BreachAssessment</code>: For assessing the severity and impact of breaches</li>
|
|
343
|
+
</ul>
|
|
344
|
+
<p className="mt-4">
|
|
345
|
+
For detailed documentation, see the{' '}
|
|
346
|
+
<Link href="/docs/components/breach-management" className="text-blue-600 hover:underline">
|
|
347
|
+
Breach Management documentation
|
|
348
|
+
</Link>
|
|
349
|
+
.
|
|
350
|
+
</p>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}
|