@tantainnovative/ndpr-toolkit 1.0.3 → 1.0.5
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/packages/ndpr-toolkit/dist/components/breach/BreachNotificationManager.d.ts +62 -0
- package/packages/ndpr-toolkit/dist/components/breach/BreachReportForm.d.ts +66 -0
- package/packages/ndpr-toolkit/dist/components/breach/BreachRiskAssessment.d.ts +50 -0
- package/packages/ndpr-toolkit/dist/components/breach/RegulatoryReportGenerator.d.ts +94 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentBanner.d.ts +79 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentManager.d.ts +73 -0
- package/packages/ndpr-toolkit/dist/components/consent/ConsentStorage.d.ts +41 -0
- package/packages/ndpr-toolkit/dist/components/dpia/DPIAQuestionnaire.d.ts +70 -0
- package/packages/ndpr-toolkit/dist/components/dpia/DPIAReport.d.ts +40 -0
- package/packages/ndpr-toolkit/dist/components/dpia/StepIndicator.d.ts +64 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRDashboard.d.ts +58 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRRequestForm.d.ts +74 -0
- package/packages/ndpr-toolkit/dist/components/dsr/DSRTracker.d.ts +56 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyExporter.d.ts +65 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyGenerator.d.ts +54 -0
- package/packages/ndpr-toolkit/dist/components/policy/PolicyPreview.d.ts +71 -0
- package/packages/ndpr-toolkit/dist/hooks/useBreach.d.ts +97 -0
- package/packages/ndpr-toolkit/dist/hooks/useConsent.d.ts +63 -0
- package/packages/ndpr-toolkit/dist/hooks/useDPIA.d.ts +92 -0
- package/packages/ndpr-toolkit/dist/hooks/useDSR.d.ts +72 -0
- package/packages/ndpr-toolkit/dist/hooks/usePrivacyPolicy.d.ts +87 -0
- package/packages/ndpr-toolkit/dist/index.d.ts +31 -0
- package/packages/ndpr-toolkit/dist/index.esm.js +2 -0
- package/packages/ndpr-toolkit/dist/index.esm.js.map +1 -0
- package/packages/ndpr-toolkit/dist/index.js +2 -0
- package/packages/ndpr-toolkit/dist/index.js.map +1 -0
- package/packages/ndpr-toolkit/dist/setupTests.d.ts +2 -0
- package/packages/ndpr-toolkit/dist/types/breach.d.ts +239 -0
- package/packages/ndpr-toolkit/dist/types/consent.d.ts +95 -0
- package/packages/ndpr-toolkit/dist/types/dpia.d.ts +196 -0
- package/packages/ndpr-toolkit/dist/types/dsr.d.ts +162 -0
- package/packages/ndpr-toolkit/dist/types/privacy.d.ts +204 -0
- package/packages/ndpr-toolkit/dist/utils/breach.d.ts +14 -0
- package/packages/ndpr-toolkit/dist/utils/consent.d.ts +10 -0
- package/packages/ndpr-toolkit/dist/utils/dpia.d.ts +12 -0
- package/packages/ndpr-toolkit/dist/utils/dsr.d.ts +11 -0
- package/packages/ndpr-toolkit/dist/utils/privacy.d.ts +12 -0
- package/src/components/consent/ConsentBanner.tsx +82 -48
- package/src/components/data-subject-rights/DataSubjectRequestForm.tsx +240 -129
- package/src/components/dpia/DPIAQuestionnaire.tsx +162 -122
- package/src/components/privacy-policy/PolicyGenerator.tsx +5 -5
- package/src/components/privacy-policy/steps/CustomSectionsStep.tsx +103 -77
- package/src/components/privacy-policy/steps/PolicyPreviewStep.tsx +117 -63
- package/src/hooks/useConsent.ts +16 -10
- package/src/lib/consentService.ts +44 -37
- package/src/lib/dpiaQuestions.ts +139 -99
- package/src/lib/requestService.ts +21 -17
- package/src/types/index.ts +13 -8
- 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/jest.config.js +0 -31
- package/jest.setup.js +0 -15
- package/packages/ndpr-toolkit/jest.config.js +0 -23
- 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/src/__tests__/example.test.ts +0 -13
- package/src/__tests__/requestService.test.ts +0 -57
- 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/components/docs/DocLayout.tsx +0 -289
- package/src/components/docs/index.ts +0 -2
package/src/hooks/useConsent.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect } from
|
|
4
|
-
import { ConsentRecord } from
|
|
5
|
-
import consentService from
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { ConsentRecord } from "@/types";
|
|
5
|
+
import consentService from "@/lib/consentService";
|
|
6
6
|
|
|
7
7
|
export function useConsent() {
|
|
8
|
-
const [consentRecord, setConsentRecord] = useState<ConsentRecord | null>(
|
|
8
|
+
const [consentRecord, setConsentRecord] = useState<ConsentRecord | null>(
|
|
9
|
+
null,
|
|
10
|
+
);
|
|
9
11
|
const [isLoading, setIsLoading] = useState(true);
|
|
10
12
|
const [showBanner, setShowBanner] = useState(false);
|
|
11
13
|
|
|
@@ -14,7 +16,7 @@ export function useConsent() {
|
|
|
14
16
|
const storedConsent = consentService.getCurrentConsent();
|
|
15
17
|
setConsentRecord(storedConsent);
|
|
16
18
|
setIsLoading(false);
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
// If no consent is stored, show the banner
|
|
19
21
|
if (!storedConsent) {
|
|
20
22
|
setShowBanner(true);
|
|
@@ -29,11 +31,15 @@ export function useConsent() {
|
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
const updateConsent = (
|
|
32
|
-
consents: Record<string, boolean>,
|
|
34
|
+
consents: Record<string, boolean>,
|
|
33
35
|
changeReason?: string,
|
|
34
|
-
userId?: string
|
|
36
|
+
userId?: string,
|
|
35
37
|
) => {
|
|
36
|
-
const updatedRecord = consentService.updateConsent(
|
|
38
|
+
const updatedRecord = consentService.updateConsent(
|
|
39
|
+
consents,
|
|
40
|
+
changeReason,
|
|
41
|
+
userId,
|
|
42
|
+
);
|
|
37
43
|
setConsentRecord(updatedRecord);
|
|
38
44
|
return updatedRecord;
|
|
39
45
|
};
|
|
@@ -59,6 +65,6 @@ export function useConsent() {
|
|
|
59
65
|
updateConsent,
|
|
60
66
|
hasConsent,
|
|
61
67
|
openPreferences,
|
|
62
|
-
closePreferences
|
|
68
|
+
closePreferences,
|
|
63
69
|
};
|
|
64
70
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { ConsentRecord, ConsentHistoryEntry } from
|
|
4
|
-
import { v4 as uuidv4 } from
|
|
5
|
-
import { storage } from
|
|
3
|
+
import { ConsentRecord, ConsentHistoryEntry } from "@/types";
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
import { storage } from "./storage";
|
|
6
6
|
|
|
7
7
|
// In a real implementation, this would connect to a database
|
|
8
8
|
// For demo purposes, we're using localStorage
|
|
9
9
|
|
|
10
|
-
const CONSENT_STORAGE_KEY =
|
|
11
|
-
const CONSENT_HISTORY_KEY =
|
|
10
|
+
const CONSENT_STORAGE_KEY = "ndpr_consent_records";
|
|
11
|
+
const CONSENT_HISTORY_KEY = "ndpr_consent_history";
|
|
12
12
|
|
|
13
13
|
// Helper function to get consent history
|
|
14
14
|
const getConsentHistoryHelper = (): ConsentHistoryEntry[] => {
|
|
@@ -17,37 +17,42 @@ const getConsentHistoryHelper = (): ConsentHistoryEntry[] => {
|
|
|
17
17
|
|
|
18
18
|
export const consentService = {
|
|
19
19
|
// Save a new consent record
|
|
20
|
-
saveConsent: (
|
|
20
|
+
saveConsent: (
|
|
21
|
+
consents: Record<string, boolean>,
|
|
22
|
+
userId?: string,
|
|
23
|
+
): ConsentRecord => {
|
|
21
24
|
const consentRecord: ConsentRecord = {
|
|
22
25
|
id: uuidv4(),
|
|
23
26
|
userId,
|
|
24
27
|
consents,
|
|
25
28
|
timestamp: new Date(),
|
|
26
|
-
ipAddress:
|
|
27
|
-
userAgent:
|
|
28
|
-
|
|
29
|
+
ipAddress: "Collected server-side in real implementation",
|
|
30
|
+
userAgent:
|
|
31
|
+
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
32
|
+
version: "1.0",
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
// Store in localStorage for demo
|
|
32
36
|
// Save as current consent
|
|
33
37
|
if (!storage.setItem(CONSENT_STORAGE_KEY, consentRecord)) {
|
|
34
|
-
throw new Error(
|
|
38
|
+
throw new Error("Failed to save consent record");
|
|
35
39
|
}
|
|
36
|
-
|
|
40
|
+
|
|
37
41
|
// Add to history
|
|
38
42
|
const historyEntry: ConsentHistoryEntry = {
|
|
39
43
|
timestamp: new Date(),
|
|
40
44
|
consents,
|
|
41
|
-
action:
|
|
42
|
-
ipAddress:
|
|
43
|
-
userAgent:
|
|
44
|
-
|
|
45
|
+
action: "granted",
|
|
46
|
+
ipAddress: "Collected server-side in real implementation",
|
|
47
|
+
userAgent:
|
|
48
|
+
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
49
|
+
version: "1.0",
|
|
45
50
|
};
|
|
46
|
-
|
|
51
|
+
|
|
47
52
|
const history = getConsentHistoryHelper();
|
|
48
53
|
history.push(historyEntry);
|
|
49
54
|
if (!storage.setItem(CONSENT_HISTORY_KEY, history)) {
|
|
50
|
-
throw new Error(
|
|
55
|
+
throw new Error("Failed to save consent history");
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
return consentRecord;
|
|
@@ -65,55 +70,57 @@ export const consentService = {
|
|
|
65
70
|
|
|
66
71
|
// Update consent with change reason
|
|
67
72
|
updateConsent: (
|
|
68
|
-
consents: Record<string, boolean>,
|
|
73
|
+
consents: Record<string, boolean>,
|
|
69
74
|
changeReason?: string,
|
|
70
|
-
userId?: string
|
|
75
|
+
userId?: string,
|
|
71
76
|
): ConsentRecord => {
|
|
72
77
|
// Get the previous consent to determine the action
|
|
73
78
|
const previousConsent = consentService.getCurrentConsent();
|
|
74
|
-
|
|
79
|
+
|
|
75
80
|
// Create the new consent record
|
|
76
81
|
const consentRecord: ConsentRecord = {
|
|
77
82
|
id: uuidv4(),
|
|
78
83
|
userId,
|
|
79
84
|
consents,
|
|
80
85
|
timestamp: new Date(),
|
|
81
|
-
ipAddress:
|
|
82
|
-
userAgent:
|
|
83
|
-
|
|
86
|
+
ipAddress: "Collected server-side in real implementation",
|
|
87
|
+
userAgent:
|
|
88
|
+
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
89
|
+
version: "1.0",
|
|
84
90
|
};
|
|
85
91
|
|
|
86
92
|
// Store in localStorage for demo
|
|
87
93
|
// Save as current consent
|
|
88
94
|
if (!storage.setItem(CONSENT_STORAGE_KEY, consentRecord)) {
|
|
89
|
-
throw new Error(
|
|
95
|
+
throw new Error("Failed to save consent record");
|
|
90
96
|
}
|
|
91
|
-
|
|
97
|
+
|
|
92
98
|
// Determine the action type
|
|
93
|
-
let action:
|
|
99
|
+
let action: "granted" | "revoked" | "updated" = "updated";
|
|
94
100
|
if (!previousConsent) {
|
|
95
|
-
action =
|
|
101
|
+
action = "granted";
|
|
96
102
|
} else {
|
|
97
|
-
const allRevoked = Object.values(consents).every(v => !v);
|
|
103
|
+
const allRevoked = Object.values(consents).every((v) => !v);
|
|
98
104
|
if (allRevoked) {
|
|
99
|
-
action =
|
|
105
|
+
action = "revoked";
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
|
|
108
|
+
|
|
103
109
|
// Add to history
|
|
104
110
|
const historyEntry: ConsentHistoryEntry = {
|
|
105
111
|
timestamp: new Date(),
|
|
106
112
|
consents,
|
|
107
113
|
action,
|
|
108
|
-
ipAddress:
|
|
109
|
-
userAgent:
|
|
110
|
-
|
|
114
|
+
ipAddress: "Collected server-side in real implementation",
|
|
115
|
+
userAgent:
|
|
116
|
+
typeof window !== "undefined" ? window.navigator.userAgent : "Unknown",
|
|
117
|
+
version: "1.0",
|
|
111
118
|
};
|
|
112
|
-
|
|
119
|
+
|
|
113
120
|
const history = getConsentHistoryHelper();
|
|
114
121
|
history.push(historyEntry);
|
|
115
122
|
if (!storage.setItem(CONSENT_HISTORY_KEY, history)) {
|
|
116
|
-
throw new Error(
|
|
123
|
+
throw new Error("Failed to save consent history");
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
return consentRecord;
|
|
@@ -131,7 +138,7 @@ export const consentService = {
|
|
|
131
138
|
const current = consentService.getCurrentConsent();
|
|
132
139
|
if (!current) return false;
|
|
133
140
|
return current.consents[type] === true;
|
|
134
|
-
}
|
|
141
|
+
},
|
|
135
142
|
};
|
|
136
143
|
|
|
137
144
|
export default consentService;
|
package/src/lib/dpiaQuestions.ts
CHANGED
|
@@ -1,148 +1,188 @@
|
|
|
1
|
-
import { RiskAssessmentQuestion } from
|
|
1
|
+
import { RiskAssessmentQuestion } from "@/types";
|
|
2
2
|
|
|
3
3
|
export const dpiaQuestions: RiskAssessmentQuestion[] = [
|
|
4
4
|
{
|
|
5
|
-
id:
|
|
6
|
-
text:
|
|
7
|
-
type:
|
|
5
|
+
id: "data-collection-1",
|
|
6
|
+
text: "Does your project involve collecting personal data directly from individuals?",
|
|
7
|
+
type: "radio",
|
|
8
8
|
required: true,
|
|
9
9
|
options: [
|
|
10
|
-
{ value: "1", label:
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
{ value: "1", label: "No personal data is collected" },
|
|
11
|
+
{
|
|
12
|
+
value: "2",
|
|
13
|
+
label: "Limited personal data is collected with clear consent",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "3",
|
|
17
|
+
label: "Significant personal data is collected with consent",
|
|
18
|
+
},
|
|
19
|
+
{ value: "4", label: "Extensive personal data is collected" },
|
|
20
|
+
],
|
|
15
21
|
},
|
|
16
22
|
{
|
|
17
|
-
id:
|
|
18
|
-
text:
|
|
19
|
-
type:
|
|
23
|
+
id: "data-collection-2",
|
|
24
|
+
text: "Does your project involve collecting sensitive personal data (e.g., health, biometric, religious beliefs)?",
|
|
25
|
+
type: "radio",
|
|
20
26
|
required: true,
|
|
21
27
|
options: [
|
|
22
|
-
{ value: "1", label:
|
|
23
|
-
{ value: "2", label:
|
|
24
|
-
{ value: "3", label:
|
|
25
|
-
{ value: "4", label:
|
|
26
|
-
]
|
|
28
|
+
{ value: "1", label: "No sensitive data is collected" },
|
|
29
|
+
{ value: "2", label: "Limited sensitive data with explicit consent" },
|
|
30
|
+
{ value: "3", label: "Significant sensitive data with explicit consent" },
|
|
31
|
+
{ value: "4", label: "Extensive sensitive data collection" },
|
|
32
|
+
],
|
|
27
33
|
},
|
|
28
34
|
{
|
|
29
|
-
id:
|
|
30
|
-
text:
|
|
31
|
-
type:
|
|
35
|
+
id: "data-collection-3",
|
|
36
|
+
text: "Does your project collect data from children or vulnerable individuals?",
|
|
37
|
+
type: "radio",
|
|
32
38
|
required: true,
|
|
33
39
|
options: [
|
|
34
|
-
{ value: "1", label:
|
|
35
|
-
{ value: "2", label:
|
|
36
|
-
{ value: "3", label:
|
|
37
|
-
{ value: "4", label:
|
|
38
|
-
]
|
|
40
|
+
{ value: "1", label: "No data from children or vulnerable individuals" },
|
|
41
|
+
{ value: "2", label: "Limited data with parental/guardian consent" },
|
|
42
|
+
{ value: "3", label: "Significant data with enhanced safeguards" },
|
|
43
|
+
{ value: "4", label: "Extensive data from vulnerable groups" },
|
|
44
|
+
],
|
|
39
45
|
},
|
|
40
46
|
{
|
|
41
|
-
id:
|
|
42
|
-
text:
|
|
43
|
-
type:
|
|
47
|
+
id: "data-processing-1",
|
|
48
|
+
text: "Does your project involve automated decision-making or profiling?",
|
|
49
|
+
type: "radio",
|
|
44
50
|
required: true,
|
|
45
51
|
options: [
|
|
46
|
-
{ value: "1", label:
|
|
47
|
-
{ value: "2", label:
|
|
48
|
-
{ value: "3", label:
|
|
49
|
-
{
|
|
50
|
-
|
|
52
|
+
{ value: "1", label: "No automated decision-making" },
|
|
53
|
+
{ value: "2", label: "Limited automation with human oversight" },
|
|
54
|
+
{ value: "3", label: "Significant automation with opt-out options" },
|
|
55
|
+
{
|
|
56
|
+
value: "4",
|
|
57
|
+
label: "Extensive automated decisions affecting individuals",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
51
60
|
},
|
|
52
61
|
{
|
|
53
|
-
id:
|
|
54
|
-
text:
|
|
55
|
-
type:
|
|
62
|
+
id: "data-processing-2",
|
|
63
|
+
text: "Does your project involve processing data for purposes beyond what was initially collected for?",
|
|
64
|
+
type: "radio",
|
|
56
65
|
required: true,
|
|
57
66
|
options: [
|
|
58
|
-
{ value: "1", label:
|
|
59
|
-
{ value: "2", label:
|
|
60
|
-
{ value: "3", label:
|
|
61
|
-
{ value: "4", label:
|
|
62
|
-
]
|
|
67
|
+
{ value: "1", label: "Processing only for original purpose" },
|
|
68
|
+
{ value: "2", label: "Limited secondary processing with notice" },
|
|
69
|
+
{ value: "3", label: "Significant secondary processing with consent" },
|
|
70
|
+
{ value: "4", label: "Extensive repurposing of data" },
|
|
71
|
+
],
|
|
63
72
|
},
|
|
64
73
|
{
|
|
65
|
-
id:
|
|
66
|
-
text:
|
|
67
|
-
type:
|
|
74
|
+
id: "data-processing-3",
|
|
75
|
+
text: "Does your project combine data from multiple sources?",
|
|
76
|
+
type: "radio",
|
|
68
77
|
required: true,
|
|
69
78
|
options: [
|
|
70
|
-
{ value: "1", label:
|
|
71
|
-
{ value: "2", label:
|
|
72
|
-
{ value: "3", label:
|
|
73
|
-
{ value: "4", label:
|
|
74
|
-
]
|
|
79
|
+
{ value: "1", label: "No data combination" },
|
|
80
|
+
{ value: "2", label: "Limited combination from controlled sources" },
|
|
81
|
+
{ value: "3", label: "Significant combination with transparency" },
|
|
82
|
+
{ value: "4", label: "Extensive data aggregation from various sources" },
|
|
83
|
+
],
|
|
75
84
|
},
|
|
76
85
|
{
|
|
77
|
-
id:
|
|
78
|
-
text:
|
|
79
|
-
type:
|
|
86
|
+
id: "data-sharing-1",
|
|
87
|
+
text: "Does your project involve sharing personal data with third parties?",
|
|
88
|
+
type: "radio",
|
|
80
89
|
required: true,
|
|
81
90
|
options: [
|
|
82
|
-
{ value: "1", label:
|
|
83
|
-
{ value: "2", label:
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
{ value: "1", label: "No third-party sharing" },
|
|
92
|
+
{ value: "2", label: "Limited sharing with contractual safeguards" },
|
|
93
|
+
{
|
|
94
|
+
value: "3",
|
|
95
|
+
label: "Significant sharing with data processing agreements",
|
|
96
|
+
},
|
|
97
|
+
{ value: "4", label: "Extensive sharing with multiple third parties" },
|
|
98
|
+
],
|
|
87
99
|
},
|
|
88
100
|
{
|
|
89
|
-
id:
|
|
90
|
-
text:
|
|
91
|
-
type:
|
|
101
|
+
id: "data-sharing-2",
|
|
102
|
+
text: "Does your project involve transferring data outside Nigeria?",
|
|
103
|
+
type: "radio",
|
|
92
104
|
required: true,
|
|
93
105
|
options: [
|
|
94
|
-
{ value: "1", label:
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
{ value: "1", label: "No international transfers" },
|
|
107
|
+
{
|
|
108
|
+
value: "2",
|
|
109
|
+
label: "Limited transfers to countries with adequate protection",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
value: "3",
|
|
113
|
+
label: "Significant transfers with standard contractual clauses",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
value: "4",
|
|
117
|
+
label: "Extensive transfers to countries without adequate protection",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
99
120
|
},
|
|
100
121
|
{
|
|
101
|
-
id:
|
|
102
|
-
text:
|
|
103
|
-
type:
|
|
122
|
+
id: "security-1",
|
|
123
|
+
text: "What level of security measures does your project implement?",
|
|
124
|
+
type: "radio",
|
|
104
125
|
required: true,
|
|
105
126
|
options: [
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
127
|
+
{
|
|
128
|
+
value: "1",
|
|
129
|
+
label:
|
|
130
|
+
"Comprehensive security with encryption, access controls, and regular audits",
|
|
131
|
+
},
|
|
132
|
+
{ value: "2", label: "Strong security measures with some monitoring" },
|
|
133
|
+
{
|
|
134
|
+
value: "3",
|
|
135
|
+
label: "Basic security measures meeting minimum requirements",
|
|
136
|
+
},
|
|
137
|
+
{ value: "4", label: "Limited security measures" },
|
|
138
|
+
],
|
|
111
139
|
},
|
|
112
140
|
{
|
|
113
|
-
id:
|
|
114
|
-
text:
|
|
115
|
-
type:
|
|
141
|
+
id: "security-2",
|
|
142
|
+
text: "Does your project have a data breach response plan?",
|
|
143
|
+
type: "radio",
|
|
116
144
|
required: true,
|
|
117
145
|
options: [
|
|
118
|
-
{ value: "1", label:
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
146
|
+
{ value: "1", label: "Comprehensive breach plan with regular testing" },
|
|
147
|
+
{
|
|
148
|
+
value: "2",
|
|
149
|
+
label: "Documented breach plan with assigned responsibilities",
|
|
150
|
+
},
|
|
151
|
+
{ value: "3", label: "Basic breach notification procedure" },
|
|
152
|
+
{ value: "4", label: "No formal breach response plan" },
|
|
153
|
+
],
|
|
123
154
|
},
|
|
124
155
|
{
|
|
125
|
-
id:
|
|
126
|
-
text:
|
|
127
|
-
type:
|
|
156
|
+
id: "data-subject-rights-1",
|
|
157
|
+
text: "How does your project facilitate data subject rights (access, rectification, erasure, etc.)?",
|
|
158
|
+
type: "radio",
|
|
128
159
|
required: true,
|
|
129
160
|
options: [
|
|
130
|
-
{ value: "1", label:
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
{ value: "1", label: "Automated self-service portal for all rights" },
|
|
162
|
+
{
|
|
163
|
+
value: "2",
|
|
164
|
+
label: "Documented procedures with reasonable response times",
|
|
165
|
+
},
|
|
166
|
+
{ value: "3", label: "Basic manual process for handling requests" },
|
|
167
|
+
{ value: "4", label: "Limited or no formal process for rights requests" },
|
|
168
|
+
],
|
|
135
169
|
},
|
|
136
170
|
{
|
|
137
|
-
id:
|
|
138
|
-
text:
|
|
139
|
-
type:
|
|
171
|
+
id: "data-subject-rights-2",
|
|
172
|
+
text: "Does your project provide clear privacy information to data subjects?",
|
|
173
|
+
type: "radio",
|
|
140
174
|
required: true,
|
|
141
175
|
options: [
|
|
142
|
-
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
176
|
+
{
|
|
177
|
+
value: "1",
|
|
178
|
+
label: "Comprehensive, layered privacy notices in plain language",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
value: "2",
|
|
182
|
+
label: "Clear privacy policy with all required information",
|
|
183
|
+
},
|
|
184
|
+
{ value: "3", label: "Basic privacy notice covering essential elements" },
|
|
185
|
+
{ value: "4", label: "Minimal or complex privacy information" },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
];
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { DataSubjectRequest, RequestStatus } from
|
|
4
|
-
import { v4 as uuidv4 } from
|
|
5
|
-
import { storage } from
|
|
3
|
+
import { DataSubjectRequest, RequestStatus } from "@/types";
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
import { storage } from "./storage";
|
|
6
6
|
|
|
7
|
-
const REQUEST_STORAGE_KEY =
|
|
7
|
+
const REQUEST_STORAGE_KEY = "ndpr_requests";
|
|
8
8
|
|
|
9
9
|
const getStoredRequests = (): DataSubjectRequest[] => {
|
|
10
10
|
return storage.getItem<DataSubjectRequest[]>(REQUEST_STORAGE_KEY, []) || [];
|
|
@@ -12,16 +12,15 @@ const getStoredRequests = (): DataSubjectRequest[] => {
|
|
|
12
12
|
|
|
13
13
|
export const requestService = {
|
|
14
14
|
createRequest: (
|
|
15
|
-
requestType: DataSubjectRequest[
|
|
15
|
+
requestType: DataSubjectRequest["type"],
|
|
16
16
|
requesterName: string,
|
|
17
17
|
requesterEmail: string,
|
|
18
18
|
details: string,
|
|
19
|
-
consent: boolean
|
|
20
19
|
): DataSubjectRequest => {
|
|
21
20
|
const request: DataSubjectRequest = {
|
|
22
21
|
id: uuidv4(),
|
|
23
22
|
type: requestType,
|
|
24
|
-
status:
|
|
23
|
+
status: "pending",
|
|
25
24
|
createdAt: Date.now(),
|
|
26
25
|
updatedAt: Date.now(),
|
|
27
26
|
subject: {
|
|
@@ -34,33 +33,38 @@ export const requestService = {
|
|
|
34
33
|
const requests = getStoredRequests();
|
|
35
34
|
requests.push(request);
|
|
36
35
|
const saved = storage.setItem(REQUEST_STORAGE_KEY, requests);
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
if (!saved) {
|
|
39
|
-
throw new Error(
|
|
38
|
+
throw new Error(
|
|
39
|
+
"Failed to save request. Storage may be full or unavailable.",
|
|
40
|
+
);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
return request;
|
|
43
44
|
},
|
|
44
45
|
|
|
45
|
-
updateStatus: (
|
|
46
|
+
updateStatus: (
|
|
47
|
+
id: string,
|
|
48
|
+
status: RequestStatus,
|
|
49
|
+
): DataSubjectRequest | null => {
|
|
46
50
|
const requests = getStoredRequests();
|
|
47
|
-
const idx = requests.findIndex(r => r.id === id);
|
|
51
|
+
const idx = requests.findIndex((r) => r.id === id);
|
|
48
52
|
if (idx === -1) return null;
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
requests[idx].status = status;
|
|
51
55
|
requests[idx].updatedAt = Date.now();
|
|
52
|
-
|
|
56
|
+
|
|
53
57
|
const saved = storage.setItem(REQUEST_STORAGE_KEY, requests);
|
|
54
58
|
if (!saved) {
|
|
55
|
-
throw new Error(
|
|
59
|
+
throw new Error("Failed to update request status.");
|
|
56
60
|
}
|
|
57
|
-
|
|
61
|
+
|
|
58
62
|
return requests[idx];
|
|
59
63
|
},
|
|
60
64
|
|
|
61
65
|
getRequest: (id: string): DataSubjectRequest | null => {
|
|
62
66
|
const requests = getStoredRequests();
|
|
63
|
-
return requests.find(r => r.id === id) || null;
|
|
67
|
+
return requests.find((r) => r.id === id) || null;
|
|
64
68
|
},
|
|
65
69
|
|
|
66
70
|
getAllRequests: (): DataSubjectRequest[] => {
|
package/src/types/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Re-export all types from the package
|
|
2
2
|
// This ensures consistency between the main app and the package
|
|
3
|
-
export * from
|
|
4
|
-
import type { DSRStatus, DSRType } from
|
|
3
|
+
export * from "@tantainnovative/ndpr-toolkit";
|
|
4
|
+
import type { DSRStatus, DSRType } from "@tantainnovative/ndpr-toolkit";
|
|
5
5
|
|
|
6
6
|
// Additional app-specific types that extend the package types
|
|
7
7
|
export interface AppConfig {
|
|
8
8
|
apiUrl?: string;
|
|
9
|
-
environment:
|
|
9
|
+
environment: "development" | "staging" | "production";
|
|
10
10
|
features: {
|
|
11
11
|
consent: boolean;
|
|
12
12
|
dsr: boolean;
|
|
@@ -38,14 +38,19 @@ export type {
|
|
|
38
38
|
OrganizationInfo,
|
|
39
39
|
DPIAQuestion as RiskAssessmentQuestion,
|
|
40
40
|
DPIASection,
|
|
41
|
-
DPIAResult
|
|
42
|
-
} from
|
|
41
|
+
DPIAResult,
|
|
42
|
+
} from "@tantainnovative/ndpr-toolkit";
|
|
43
43
|
|
|
44
44
|
// Define ConsentType locally since it's not exported from the package
|
|
45
|
-
export type ConsentType =
|
|
45
|
+
export type ConsentType =
|
|
46
|
+
| "necessary"
|
|
47
|
+
| "functional"
|
|
48
|
+
| "analytics"
|
|
49
|
+
| "marketing"
|
|
50
|
+
| "preferences";
|
|
46
51
|
|
|
47
52
|
// Define BreachSeverity type
|
|
48
|
-
export type BreachSeverity =
|
|
53
|
+
export type BreachSeverity = "low" | "medium" | "high" | "critical";
|
|
49
54
|
|
|
50
55
|
// Define missing types that are not exported from the package
|
|
51
56
|
export interface ConsentRecord {
|
|
@@ -61,7 +66,7 @@ export interface ConsentRecord {
|
|
|
61
66
|
export interface ConsentHistoryEntry {
|
|
62
67
|
timestamp: Date;
|
|
63
68
|
consents: Record<string, boolean>;
|
|
64
|
-
action:
|
|
69
|
+
action: "granted" | "revoked" | "updated";
|
|
65
70
|
ipAddress?: string;
|
|
66
71
|
userAgent?: string;
|
|
67
72
|
version: string;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(find:*)",
|
|
5
|
-
"Bash(grep:*)",
|
|
6
|
-
"Bash(git push:*)",
|
|
7
|
-
"Bash(npm run lint)",
|
|
8
|
-
"Bash(pnpm lint:*)",
|
|
9
|
-
"Bash(pnpm install:*)",
|
|
10
|
-
"Bash(git add:*)",
|
|
11
|
-
"Bash(git commit:*)",
|
|
12
|
-
"Bash(pnpm add:*)",
|
|
13
|
-
"Bash(pnpm build:*)",
|
|
14
|
-
"Bash(sed:*)",
|
|
15
|
-
"Bash(cat:*)",
|
|
16
|
-
"Bash(npm:*)"
|
|
17
|
-
],
|
|
18
|
-
"deny": []
|
|
19
|
-
}
|
|
20
|
-
}
|