@tantainnovative/ndpr-toolkit
Version:
Nigeria Data Protection Toolkit — enterprise-grade compliance components for the Nigeria Data Protection Act (NDPA) 2023
729 lines (696 loc) • 23.7 kB
text/typescript
import * as React_2 from 'react';
import React__default from 'react';
export declare const DSR: {
Provider: React_2.FC<DSRProviderProps>;
Form: React_2.FC<DSRRequestFormProps>;
Dashboard: React_2.FC<DSRDashboardProps>;
Tracker: React_2.FC<DSRTrackerProps>;
};
declare interface DSRContextValue extends UseDSRReturn {
requestTypes: RequestType[];
}
/**
* Data Subject Request dashboard component. Supports compliance with NDPA Part IV,
* providing tools to track, manage, and respond to data subject requests within required timeframes.
*/
export declare const DSRDashboard: React__default.FC<DSRDashboardProps>;
export declare interface DSRDashboardClassNames {
root?: string;
header?: string;
title?: string;
filters?: string;
requestList?: string;
requestItem?: string;
statusBadge?: string;
detailPanel?: string;
}
declare interface DSRDashboardProps {
/**
* List of DSR requests to display
*/
requests: DSRRequest[];
/**
* Callback function called when a request is selected
*/
onSelectRequest?: (requestId: string) => void;
/**
* Callback function called when a request status is updated
*/
onUpdateStatus?: (requestId: string, status: DSRStatus) => void;
/**
* Callback function called when a request is assigned
*/
onAssignRequest?: (requestId: string, assignee: string) => void;
/**
* Title displayed on the dashboard
* @default "Data Subject Request Dashboard"
*/
title?: string;
/**
* Description text displayed on the dashboard
* @default "Track and manage data subject requests in compliance with NDPA Part IV requirements."
*/
description?: string;
/**
* Custom CSS class for the dashboard
*/
className?: string;
/**
* Custom CSS class for the buttons
*/
buttonClassName?: string;
/**
* Whether to show the request details
* @default true
*/
showRequestDetails?: boolean;
/**
* Whether to show the request timeline
* @default true
*/
showRequestTimeline?: boolean;
/**
* Whether to show the deadline alerts
* @default true
*/
showDeadlineAlerts?: boolean;
/**
* List of possible assignees
*/
assignees?: string[];
/**
* Object of CSS class overrides keyed by semantic section name.
*/
classNames?: DSRDashboardClassNames;
/**
* When true, all default Tailwind classes are removed so consumers
* can style from scratch using classNames.
*/
unstyled?: boolean;
}
/**
* Represents the data submitted by the DSR request form.
*/
export declare interface DSRFormSubmission {
/** The selected request type identifier */
requestType: string;
/** Data subject personal information */
dataSubject: {
fullName: string;
email: string;
phone?: string;
identifierType: string;
identifierValue: string;
};
/** Additional information provided for the selected request type */
additionalInfo?: Record<string, string | number | boolean | null>;
/** Timestamp (ms) when the form was submitted */
submittedAt: number;
}
export declare const DSRProvider: React__default.FC<DSRProviderProps>;
export declare interface DSRProviderProps {
requestTypes: RequestType[];
adapter?: StorageAdapter<DSRRequest[]>;
storageKey?: string;
useLocalStorage?: boolean;
onSubmit?: (request: DSRRequest) => void;
onUpdate?: (request: DSRRequest) => void;
children: React__default.ReactNode;
}
/**
* Represents a data subject request
*/
export declare interface DSRRequest {
/** Unique identifier for the request */
id: string;
/** Type of request */
type: DSRType;
/** Current status of the request */
status: DSRStatus;
/** Timestamp when the request was submitted */
createdAt: number;
/** Timestamp when the request was last updated */
updatedAt: number;
/** Timestamp when the request was completed (if applicable) */
completedAt?: number;
/** Timestamp when the identity was verified (if applicable) */
verifiedAt?: number;
/**
* Due date for responding to the request (timestamp)
* NDPA requires response within 30 days of receipt
*/
dueDate?: number;
/** Description or details of the request */
description?: string;
/**
* The lawful basis under which the data was originally processed
* Relevant for evaluating objection and erasure requests
*/
lawfulBasis?: string;
/** Data subject information */
subject: {
name: string;
email: string;
phone?: string;
identifierValue?: string;
identifierType?: string;
};
/** Additional information provided by the data subject */
additionalInfo?: Record<string, string | number | boolean | null>;
/** Notes added by staff processing the request */
internalNotes?: Array<{
timestamp: number;
author: string;
note: string;
}>;
/** Verification status */
verification?: {
verified: boolean;
method?: string;
verifiedAt?: number;
verifiedBy?: string;
};
/** Reason for rejection (if status is 'rejected') */
rejectionReason?: string;
/** Files attached to the request */
attachments?: Array<{
id: string;
name: string;
type: string;
url: string;
addedAt: number;
}>;
/**
* Whether an extension was requested for this DSR
* NDPA allows a one-time extension of 30 days with justification
*/
extensionRequested?: boolean;
/** Reason for the extension, if requested */
extensionReason?: string;
}
/**
* Data Subject Request form component. Implements NDPA Part VI (Sections 34-38)
* covering data subject rights including access, rectification, erasure, and portability.
*/
export declare const DSRRequestForm: React__default.FC<DSRRequestFormProps>;
export declare interface DSRRequestFormClassNames {
root?: string;
title?: string;
description?: string;
form?: string;
fieldGroup?: string;
label?: string;
input?: string;
select?: string;
textarea?: string;
submitButton?: string;
/** Alias for submitButton */
primaryButton?: string;
successMessage?: string;
/** Custom class applied when isSubmitting is true (e.g. a loading overlay) */
loadingOverlay?: string;
}
declare interface DSRRequestFormProps {
/**
* Array of request types that can be submitted
*/
requestTypes: RequestType[];
/**
* Callback function called when form is submitted
*/
onSubmit: (data: DSRFormSubmission) => void;
/**
* Callback function called when form validation fails
*/
onValidationError?: (errors: Record<string, string>) => void;
/**
* Title displayed on the form
* @default "Submit a Data Subject Request"
*/
title?: string;
/**
* Description text displayed on the form
* @default "Use this form to exercise your rights under the Nigeria Data Protection Act (NDPA), Part VI, Sections 34-38."
*/
description?: string;
/**
* Text for the submit button
* @default "Submit Request"
*/
submitButtonText?: string;
/**
* Custom CSS class for the form
*/
className?: string;
/**
* Custom CSS class for the submit button
*/
buttonClassName?: string;
/**
* Whether to show a confirmation message after submission
* @default true
*/
showConfirmation?: boolean;
/**
* Confirmation message to display after submission
* @default "Your request has been submitted successfully. You will receive a confirmation email shortly."
*/
confirmationMessage?: string;
/**
* Whether to require identity verification
* @default true
*/
requireIdentityVerification?: boolean;
/**
* Types of identifiers accepted for verification
* @default ["email", "account", "customer_id"]
*/
identifierTypes?: Array<{
id: string;
label: string;
}>;
/**
* Whether to collect additional contact information
* @default true
*/
collectAdditionalContact?: boolean;
/**
* Custom labels for form fields
*/
labels?: {
name?: string;
email?: string;
requestType?: string;
description?: string;
submit?: string;
};
/**
* Object of CSS class overrides keyed by semantic section name.
*/
classNames?: DSRRequestFormClassNames;
/**
* When true, all default Tailwind classes are removed so consumers
* can style from scratch using classNames.
*/
unstyled?: boolean;
/**
* Whether the form is currently submitting.
* When true, the submit button is disabled and shows "Submitting..." text.
*/
isSubmitting?: boolean;
/**
* Default values to pre-fill form fields.
* Useful for editing existing requests or pre-populating known data.
*/
defaultValues?: Partial<DSRFormSubmission>;
/**
* Callback fired when the form is reset via the Reset button.
* To fully remount the component (clearing all internal state),
* change the `key` prop from the parent.
*/
onReset?: () => void;
}
/**
* Status of a data subject request
*/
export declare type DSRStatus = 'pending' | 'awaitingVerification' | 'inProgress' | 'completed' | 'rejected';
/**
* Validated DSR submission shape — matches what `<DSRRequestForm onSubmit>`
* emits client-side. Use this as the typed parameter for your server-side
* handler after `validateDsrSubmissionStructured` returns `valid: true`.
*/
export declare interface DsrSubmissionPayload {
requestType: string;
dataSubject: {
fullName: string;
email: string;
phone?: string;
identifierType: string;
identifierValue: string;
};
additionalInfo?: Record<string, string | number | boolean | null>;
submittedAt: number;
}
/**
* DSR tracking and analytics component. Supports compliance with NDPA Part IV,
* providing summary statistics, deadline tracking, and compliance metrics for data subject requests.
*/
export declare const DSRTracker: React__default.FC<DSRTrackerProps>;
export declare interface DSRTrackerClassNames {
root?: string;
header?: string;
title?: string;
stats?: string;
statCard?: string;
table?: string;
tableHeader?: string;
tableRow?: string;
statusBadge?: string;
}
declare interface DSRTrackerProps {
/**
* List of DSR requests to track
*/
requests: DSRRequest[];
/**
* Callback function called when a request is selected
*/
onSelectRequest?: (requestId: string) => void;
/**
* Title displayed on the tracker
* @default "DSR Request Tracker"
*/
title?: string;
/**
* Description text displayed on the tracker
* @default "Track the status and progress of data subject requests as required by NDPA Part IV."
*/
description?: string;
/**
* Custom CSS class for the tracker
*/
className?: string;
/**
* Custom CSS class for the buttons
*/
buttonClassName?: string;
/**
* Whether to show the summary statistics
* @default true
*/
showSummaryStats?: boolean;
/**
* Whether to show the request type breakdown
* @default true
*/
showTypeBreakdown?: boolean;
/**
* Whether to show the status breakdown
* @default true
*/
showStatusBreakdown?: boolean;
/**
* Whether to show the timeline chart
* @default true
*/
showTimelineChart?: boolean;
/**
* Whether to show the overdue requests
* @default true
*/
showOverdueRequests?: boolean;
/**
* Object of CSS class overrides keyed by semantic section name.
*/
classNames?: DSRTrackerClassNames;
/**
* When true, all default Tailwind classes are removed so consumers
* can style from scratch using classNames.
*/
unstyled?: boolean;
}
/**
* Data Subject Rights types aligned with NDPA 2023 Part VI (Sections 34-38)
* and the related provisions in Part V (Section 27 — information to the data subject)
* and Part X (Section 46 — complaint to the Commission).
*
* Note: These are guidance labels — not legal advice. Verify with your DPO or counsel.
*/
/**
* Types of data subject requests per NDPA Part VI
* - 'information': Right to be informed (Section 27 — provision of information; Section 34(1)(a))
* - 'access': Right of access / confirmation + data copy (Section 34(1)(a)–(b))
* - 'rectification': Right to rectification (Section 34(1)(c))
* - 'erasure': Right to erasure (Section 34(1)(d), Section 34(2))
* - 'restriction': Right to restrict processing (Section 34(1)(e))
* - 'portability': Right to data portability (Section 38)
* - 'objection': Right to object (Section 36)
* - 'automated_decision_making': Rights re. automated decisions / profiling (Section 37)
* - 'withdraw_consent': Right to withdraw consent (Section 35)
*/
export declare type DSRType = 'information' | 'access' | 'rectification' | 'erasure' | 'restriction' | 'portability' | 'objection' | 'automated_decision_making' | 'withdraw_consent';
/**
* Format a DSR request for display or submission. Returns the formatted
* payload plus a typed `errors` array of `{ field, code, message }` for any
* required fields missing from the source request.
*
* Codes emitted:
* - `request_id_required`
* - `request_type_required`
* - `request_status_required`
* - `created_at_required`
* - `subject_name_required`
* - `subject_email_required`
*/
export declare function formatDSRRequestStructured(request: DSRRequest): FormatDSRRequestStructuredResult;
/** Result of {@link formatDSRRequestStructured}. */
export declare interface FormatDSRRequestStructuredResult {
valid: boolean;
errors: StructuredValidationError[];
/** Formatted request payload — always populated regardless of `valid`. */
formattedRequest: Record<string, unknown>;
/** Narrowed input — populated only on `valid: true`. */
data?: DSRRequest;
}
/**
* Legacy status of a data subject request
* @deprecated Use DSRStatus instead
*/
export declare type RequestStatus = 'pending' | 'verifying' | 'processing' | 'completed' | 'rejected';
/**
* Represents a type of data subject request (detailed configuration)
*/
export declare interface RequestType {
/** Unique identifier for the request type */
id: string;
/** Display name for the request type */
name: string;
/** Description of what this request type entails */
description: string;
/**
* NDPA 2023 section reference for this right
* (e.g., "Section 34(1)(a)" for access, "Section 38" for portability).
* Used for display purposes only — verify the exact subsection with counsel.
*/
ndpaSection?: string;
/**
* Estimated time to fulfill this type of request (in days)
* NDPA requires response within 30 days
*/
estimatedCompletionTime: number;
/** Whether additional information is required for this request type */
requiresAdditionalInfo: boolean;
/** Custom fields required for this request type */
additionalFields?: Array<{
id: string;
label: string;
type: 'text' | 'textarea' | 'select' | 'checkbox' | 'file';
options?: string[];
required: boolean;
placeholder?: string;
}>;
}
export declare interface StorageAdapter<T = unknown> {
/** Load persisted data. Called once on hook mount. */
load(): T | null | Promise<T | null>;
/** Persist data. Called on every state change. */
save(data: T): void | Promise<void>;
/** Clear persisted data. Called on reset. */
remove(): void | Promise<void>;
}
/**
* Single structured validation error with a stable, locale-independent
* `code` consumers can switch on programmatically.
*/
declare interface StructuredValidationError {
/** Dot-path of the offending field (e.g. `'timestamp'`, `'dataSubject.email'`, `'options[0].purpose'`). */
field: string;
/** Stable, snake_case error code — safe to switch on across locales. */
code: string;
/** Human-readable English message — informational only; do not regex-match. */
message: string;
}
/**
* Result of a structured validator. `errors` is an array (one entry per
* failed rule). `data` is the narrowed, typed payload, only populated on
* `valid: true`.
*/
declare interface StructuredValidationResult<T> {
valid: boolean;
errors: StructuredValidationError[];
data?: T;
}
/**
* Hook for managing Data Subject Requests in compliance with the NDPA.
*
* @example
* ```tsx
* import { useDSR } from '@tantainnovative/ndpr-toolkit/hooks';
*
* function DSRPanel() {
* const { requests, submitRequest } = useDSR({
* requestTypes: [
* { id: 'access', name: 'Access', description: 'Request access', estimatedCompletionTime: 30 },
* ],
* });
* return (
* <ul>
* {requests.map((r) => (
* <li key={r.id}>{r.type} — {r.status}</li>
* ))}
* </ul>
* );
* }
* ```
*/
export declare function useDSR({ initialRequests, requestTypes, adapter, storageKey, useLocalStorage, onSubmit, onUpdate, }: UseDSROptions): UseDSRReturn;
export declare function useDSRCompound(): DSRContextValue;
declare interface UseDSROptions {
/**
* Initial requests to load
*/
initialRequests?: DSRRequest[];
/**
* Available request types
*/
requestTypes: RequestType[];
/**
* Pluggable storage adapter. When provided, takes precedence over storageKey/useLocalStorage.
*/
adapter?: StorageAdapter<DSRRequest[]>;
/**
* Storage key for requests
* @default "ndpr_dsr_requests"
* @deprecated Use adapter instead
*/
storageKey?: string;
/**
* Whether to use local storage to persist requests.
*
* **Changed in 4.0:** the default is now `false`. `useDSR` is the admin
* tracker hook and its state contains data subjects' PII; the previous
* default (true) stored that PII in the admin's browser localStorage,
* which is rarely appropriate. Opt in by passing `useLocalStorage: true`
* if you specifically want the old behaviour.
*
* For production deployments, pass an explicit `adapter` instead.
*
* @default false (as of 4.0; was `true` in 3.x)
* @deprecated Pass an explicit `adapter` instead of toggling this flag.
*/
useLocalStorage?: boolean;
/**
* Callback function called when a request is submitted
*/
onSubmit?: (request: DSRRequest) => void;
/**
* Callback function called when a request is updated
*/
onUpdate?: (request: DSRRequest) => void;
}
declare interface UseDSRReturn {
/**
* All requests
*/
requests: DSRRequest[];
/**
* Submit a new request. The hook assigns `id`, `status`, `createdAt`,
* `updatedAt`, and `dueDate` — pass everything else.
*/
submitRequest: (requestData: Omit<DSRRequest, 'id' | 'status' | 'createdAt' | 'updatedAt' | 'dueDate'>) => DSRRequest;
/**
* Update an existing request
*/
updateRequest: (id: string, updates: Partial<DSRRequest>) => DSRRequest | null;
/**
* Get a request by ID
*/
getRequest: (id: string) => DSRRequest | null;
/**
* Get requests by status
*/
/**
* Filter requests by status. Accepts both the modern `DSRStatus` union and
* the deprecated `RequestStatus` for backward compatibility — pass the
* modern values (`'pending' | 'awaitingVerification' | 'inProgress' | ...`).
*/
getRequestsByStatus: (status: DSRStatus | RequestStatus) => DSRRequest[];
/**
* Get requests by type
*/
getRequestsByType: (type: string) => DSRRequest[];
/**
* Get the request type definition by ID
*/
getRequestType: (typeId: string) => RequestType | undefined;
/**
* Format a request for display or submission
*/
formatRequest: (request: DSRRequest) => Record<string, unknown>;
/**
* Clear all requests
*/
clearRequests: () => void;
/**
* Whether the adapter is still loading data (relevant for async adapters)
*/
isLoading: boolean;
}
/** Options for {@link validateDsrSubmissionStructured}. */
export declare interface ValidateDsrSubmissionOptions {
/**
* Whether the data subject is required to provide an identifier
* (NDPC's recommended verification step). Mirror whatever you set on
* the client-side `<DSRRequestForm requireIdentityVerification>`.
* @default true
*/
requireIdentityVerification?: boolean;
/**
* Allowed request types. When provided, the payload's `requestType`
* must be one of these — useful for locking the server to a specific
* set of supported NDPA Part VI §34-38 (plus §35, §36, §37) data-subject rights.
*/
allowedRequestTypes?: string[];
}
/**
* Validate a raw DSR submission payload against the same rules
* `<DSRRequestForm />` enforces client-side. Designed to be called from a
* server-side handler (Next.js Route Handler, NestJS controller, Express
* middleware, Cloudflare Worker) so client and server stay in sync without
* the consumer hand-rolling zod / class-validator schemas.
*
* Defensive — accepts `unknown` and narrows. Safe to call directly on
* `await request.json()`. Returns `{ field, code, message }[]` errors so
* callers can switch on `code` programmatically across locales.
*
* Codes emitted:
* - `payload_not_object`
* - `request_type_required`
* - `request_type_not_allowed`
* - `data_subject_required`
* - `full_name_required`
* - `email_required`
* - `email_invalid_format`
* - `phone_invalid_type`
* - `identifier_type_required`
* - `identifier_value_required`
* - `submitted_at_invalid`
* - `additional_info_invalid_type`
* - `payload_final_narrowing_failed`
*
* @example **Next.js Route Handler**
* ```ts
* import { validateDsrSubmissionStructured } from '@tantainnovative/ndpr-toolkit/server';
*
* export async function POST(req: Request) {
* const { valid, errors, data } = validateDsrSubmissionStructured(await req.json());
* if (!valid) {
* return Response.json({ errors }, { status: 422 });
* }
* await dsrStore.create(data);
* return Response.json({ ok: true }, { status: 201 });
* }
* ```
*/
export declare function validateDsrSubmissionStructured(payload: unknown, options?: ValidateDsrSubmissionOptions): StructuredValidationResult<DsrSubmissionPayload>;
export { }