@redacto.io/consent-sdk-react 4.0.0 → 5.1.0
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/CHANGELOG.md +19 -1
- package/LICENSE +176 -0
- package/README.md +98 -1
- package/dist/index.js +231 -96
- package/dist/index.mjs +231 -96
- package/dist/privacy-center.d.mts +649 -0
- package/dist/privacy-center.d.ts +649 -0
- package/dist/privacy-center.js +20824 -0
- package/dist/privacy-center.mjs +20789 -0
- package/package.json +47 -3
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
- package/src/RedactoNoticeConsent/api/index.ts +2 -0
- package/src/RedactoNoticeConsent/api/types.ts +2 -0
- package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
- package/src/RedactoNoticeConsent/language-codes.ts +97 -0
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
- package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
- package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
- package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
- package/src/RedactoPrivacyCenter/api/client.ts +524 -0
- package/src/RedactoPrivacyCenter/api/config.ts +33 -0
- package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
- package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
- package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
- package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
- package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
- package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
- package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +38 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
- package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
- package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +304 -0
- package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
- package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
- package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
- package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
- package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
- package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +159 -0
- package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
- package/src/RedactoPrivacyCenter/components/types.ts +76 -0
- package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
- package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
- package/src/RedactoPrivacyCenter/config/table.ts +25 -0
- package/src/RedactoPrivacyCenter/context/AuthContext.tsx +137 -0
- package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
- package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
- package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -0
- package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
- package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
- package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
- package/src/RedactoPrivacyCenter/context/types.ts +14 -0
- package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
- package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
- package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
- package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
- package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
- package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
- package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
- package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
- package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
- package/src/RedactoPrivacyCenter/styles/base.css +117 -0
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
- package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
- package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
- package/src/RedactoPrivacyCenter/table/index.ts +2 -0
- package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
- package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
- package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -0
- package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
- package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
- package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
- package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
- package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
- package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +107 -0
- package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
- package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
- package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
- package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
- package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
- package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -0
- package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
- package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
- package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
- package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
- package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +53 -0
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -0
- package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
- package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
- package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
- package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
- package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
- package/src/privacy-center.ts +108 -0
- package/tsconfig.json +2 -0
- package/tsup.config.ts +16 -1
- package/.turbo/turbo-build.log +0 -21
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { apiFetch } from "./fetcher";
|
|
2
|
+
import type { ApiFetchOptions, ApiResponse } from "./fetcher";
|
|
3
|
+
import type {
|
|
4
|
+
CaseMessage,
|
|
5
|
+
MessageDocument,
|
|
6
|
+
UploadDocumentResponse,
|
|
7
|
+
} from "../lib/types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base request parameters for consent service calls
|
|
11
|
+
*/
|
|
12
|
+
export type BaseRequest = {
|
|
13
|
+
organisationUuid: string;
|
|
14
|
+
workspaceUuid: string;
|
|
15
|
+
authToken: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Consent service client factory.
|
|
20
|
+
* Creates a client instance with a specific base URL.
|
|
21
|
+
* @param baseUrl - Consent server base URL
|
|
22
|
+
*/
|
|
23
|
+
export function createConsentClient(baseUrl: string) {
|
|
24
|
+
/**
|
|
25
|
+
* Privacy Center API fetch wrapper that uses Authorization: Bearer header.
|
|
26
|
+
* Privacy center endpoints require JWT tokens in Authorization header, not X-Auth-Token.
|
|
27
|
+
*/
|
|
28
|
+
async function privacyCenterApiFetch<T>(
|
|
29
|
+
url: string,
|
|
30
|
+
options: ApiFetchOptions & { authToken?: string }
|
|
31
|
+
): Promise<T> {
|
|
32
|
+
const { authToken, getAuthToken, headers = {}, ...fetchOptions } = options;
|
|
33
|
+
|
|
34
|
+
// Build headers using Headers object
|
|
35
|
+
const requestHeaders = new Headers();
|
|
36
|
+
|
|
37
|
+
// Set default headers (skip Content-Type for FormData)
|
|
38
|
+
const isFormData = fetchOptions.body instanceof FormData;
|
|
39
|
+
if (!isFormData) {
|
|
40
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
41
|
+
}
|
|
42
|
+
requestHeaders.set("Accept", "application/json");
|
|
43
|
+
|
|
44
|
+
// Add custom headers from options
|
|
45
|
+
if (headers && typeof headers === "object") {
|
|
46
|
+
if (headers instanceof Headers) {
|
|
47
|
+
headers.forEach((value, key) => {
|
|
48
|
+
requestHeaders.set(key, value);
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
Object.entries(headers as Record<string, string>).forEach(
|
|
52
|
+
([key, value]) => {
|
|
53
|
+
if (value) {
|
|
54
|
+
requestHeaders.set(key, value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Add Authorization Bearer token for privacy center APIs
|
|
62
|
+
const token = authToken || (getAuthToken ? await getAuthToken() : null);
|
|
63
|
+
if (token) {
|
|
64
|
+
const trimmedToken = token.trim();
|
|
65
|
+
// Format token with Bearer prefix if not already present
|
|
66
|
+
const bearerToken = trimmedToken.startsWith('Bearer ')
|
|
67
|
+
? trimmedToken
|
|
68
|
+
: `Bearer ${trimmedToken}`;
|
|
69
|
+
requestHeaders.set("Authorization", bearerToken);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Build fetch options
|
|
73
|
+
const fetchOptionsFinal: RequestInit = {
|
|
74
|
+
...fetchOptions,
|
|
75
|
+
headers: requestHeaders,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(url, fetchOptionsFinal);
|
|
80
|
+
|
|
81
|
+
// Handle non-OK responses
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
let errorMessage = `Request failed with status ${response.status}`;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const errorData = (await response.json()) as {
|
|
87
|
+
detail?: unknown;
|
|
88
|
+
message?: string;
|
|
89
|
+
code?: string;
|
|
90
|
+
} | null;
|
|
91
|
+
if (errorData) {
|
|
92
|
+
if ("detail" in errorData && errorData.detail) {
|
|
93
|
+
errorMessage =
|
|
94
|
+
typeof errorData.detail === "string"
|
|
95
|
+
? errorData.detail
|
|
96
|
+
: JSON.stringify(errorData.detail, null, 2);
|
|
97
|
+
} else if ("message" in errorData && errorData.message) {
|
|
98
|
+
errorMessage = errorData.message;
|
|
99
|
+
} else if ("code" in errorData && errorData.code) {
|
|
100
|
+
// e.g. { code: "empty_file" } or { code: "document_already_exists" }
|
|
101
|
+
errorMessage = errorData.code;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// If response is not JSON, try to get text
|
|
106
|
+
try {
|
|
107
|
+
const errorText = await response.text();
|
|
108
|
+
if (errorText) {
|
|
109
|
+
errorMessage = errorText;
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// Ignore parsing errors
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Prepend HTTP status so callers can branch on it without losing context
|
|
117
|
+
throw new Error(`[${response.status}] ${errorMessage}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Parse JSON response
|
|
121
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
122
|
+
if (!contentType.includes("application/json")) {
|
|
123
|
+
const textResponse = await response.text();
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Expected JSON response, got ${contentType}: ${textResponse}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const data = (await response.json()) as ApiResponse<T> | T;
|
|
130
|
+
|
|
131
|
+
// Handle wrapped API responses (code, status, detail format)
|
|
132
|
+
if (
|
|
133
|
+
typeof data === "object" &&
|
|
134
|
+
data !== null &&
|
|
135
|
+
"code" in data &&
|
|
136
|
+
"status" in data &&
|
|
137
|
+
"detail" in data
|
|
138
|
+
) {
|
|
139
|
+
const apiResponse = data as ApiResponse<T>;
|
|
140
|
+
return apiResponse.detail as T;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return data as T;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (error instanceof Error) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
throw new Error(`Unknown error occurred: ${String(error)}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function privacyCenterUrl(baseRequest: BaseRequest, path: string): string {
|
|
153
|
+
return `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center${path}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function caseUrl(baseRequest: BaseRequest, caseUuid: string, path = ''): string {
|
|
157
|
+
return privacyCenterUrl(baseRequest, `/cases/${caseUuid}${path}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveAuthToken(authToken: string | undefined, baseRequest: BaseRequest): string {
|
|
161
|
+
return authToken || baseRequest.authToken;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
/**
|
|
166
|
+
* Get form data (privacy center)
|
|
167
|
+
*/
|
|
168
|
+
async getFormData(
|
|
169
|
+
baseRequest: BaseRequest,
|
|
170
|
+
contact: string,
|
|
171
|
+
authToken?: string,
|
|
172
|
+
options?: ApiFetchOptions & { useSessionAuth?: boolean },
|
|
173
|
+
language?: string
|
|
174
|
+
): Promise<unknown> {
|
|
175
|
+
const queryParams = new URLSearchParams();
|
|
176
|
+
if (language) queryParams.append("language", language);
|
|
177
|
+
const queryString = queryParams.toString();
|
|
178
|
+
const url = privacyCenterUrl(baseRequest, `/form/data${queryString ? `?${queryString}` : ""}`);
|
|
179
|
+
const { useSessionAuth = false, getAuthToken, ...fetchOptions } = options || {};
|
|
180
|
+
|
|
181
|
+
// Case management uses session-based auth (X-Auth-Token)
|
|
182
|
+
// Privacy center uses JWT Bearer tokens (Authorization: Bearer)
|
|
183
|
+
if (useSessionAuth) {
|
|
184
|
+
return apiFetch(url, {
|
|
185
|
+
...fetchOptions,
|
|
186
|
+
method: "POST",
|
|
187
|
+
body: JSON.stringify({ contact }),
|
|
188
|
+
getAuthToken,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Privacy center: use JWT Bearer token
|
|
193
|
+
return privacyCenterApiFetch(url, {
|
|
194
|
+
...fetchOptions,
|
|
195
|
+
method: "POST",
|
|
196
|
+
body: JSON.stringify({ contact }),
|
|
197
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create case request (privacy center or case management)
|
|
203
|
+
*/
|
|
204
|
+
async createCaseRequest(
|
|
205
|
+
baseRequest: BaseRequest,
|
|
206
|
+
payload: unknown,
|
|
207
|
+
authToken?: string,
|
|
208
|
+
options?: ApiFetchOptions & { useSessionAuth?: boolean }
|
|
209
|
+
): Promise<unknown> {
|
|
210
|
+
const url = privacyCenterUrl(baseRequest, '/case');
|
|
211
|
+
const { useSessionAuth = false, getAuthToken, ...fetchOptions } = options || {};
|
|
212
|
+
|
|
213
|
+
// Case management uses session-based auth (X-Auth-Token)
|
|
214
|
+
// Privacy center uses JWT Bearer tokens (Authorization: Bearer)
|
|
215
|
+
if (useSessionAuth) {
|
|
216
|
+
return apiFetch(url, {
|
|
217
|
+
...fetchOptions,
|
|
218
|
+
method: "POST",
|
|
219
|
+
body: JSON.stringify(payload),
|
|
220
|
+
getAuthToken,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Privacy center: use JWT Bearer token
|
|
225
|
+
return privacyCenterApiFetch(url, {
|
|
226
|
+
...fetchOptions,
|
|
227
|
+
method: "POST",
|
|
228
|
+
body: JSON.stringify(payload),
|
|
229
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create OTP (public)
|
|
235
|
+
*/
|
|
236
|
+
async createOtp(
|
|
237
|
+
organisationId: string,
|
|
238
|
+
workspaceId: string,
|
|
239
|
+
email: string,
|
|
240
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
241
|
+
): Promise<unknown> {
|
|
242
|
+
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/create`;
|
|
243
|
+
return apiFetch(url, {
|
|
244
|
+
...options,
|
|
245
|
+
method: "POST",
|
|
246
|
+
body: JSON.stringify({ to: email, channel: "email" }),
|
|
247
|
+
});
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Verify OTP (public)
|
|
252
|
+
*/
|
|
253
|
+
async verifyOtp(
|
|
254
|
+
organisationId: string,
|
|
255
|
+
workspaceId: string,
|
|
256
|
+
otpId: string,
|
|
257
|
+
otp: string,
|
|
258
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
259
|
+
): Promise<unknown> {
|
|
260
|
+
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/verify`;
|
|
261
|
+
return apiFetch(url, {
|
|
262
|
+
...options,
|
|
263
|
+
method: "POST",
|
|
264
|
+
body: JSON.stringify({ uuid: otpId, otp }),
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Exchange one-time token for JWT tokens (public)
|
|
270
|
+
*/
|
|
271
|
+
async exchangeToken(
|
|
272
|
+
token: string,
|
|
273
|
+
): Promise<unknown> {
|
|
274
|
+
const url = `${baseUrl}/public/tokens/token-exchange?token=${encodeURIComponent(token)}`;
|
|
275
|
+
const response = await fetch(url, {
|
|
276
|
+
method: "GET",
|
|
277
|
+
headers: { "Accept": "application/json" },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
const errorData = await response.json().catch(() => null) as { detail?: string; message?: string } | null;
|
|
282
|
+
throw new Error(
|
|
283
|
+
errorData?.detail || errorData?.message || `Token exchange failed with status ${response.status}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const data = await response.json() as { code?: number; status?: string; detail?: unknown };
|
|
288
|
+
if (data && typeof data === "object" && "code" in data && "status" in data && "detail" in data) {
|
|
289
|
+
return data.detail;
|
|
290
|
+
}
|
|
291
|
+
return data;
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Refresh OTP token (public)
|
|
296
|
+
*/
|
|
297
|
+
async refreshOtpToken(
|
|
298
|
+
organisationId: string,
|
|
299
|
+
workspaceId: string,
|
|
300
|
+
refreshToken: string,
|
|
301
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
302
|
+
): Promise<unknown> {
|
|
303
|
+
const url = `${baseUrl}/public/organisations/${organisationId}/workspaces/${workspaceId}/otp/refresh`;
|
|
304
|
+
return apiFetch(url, {
|
|
305
|
+
...options,
|
|
306
|
+
method: "POST",
|
|
307
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get products
|
|
313
|
+
*/
|
|
314
|
+
async getProducts(
|
|
315
|
+
baseRequest: BaseRequest,
|
|
316
|
+
skip: number = 0,
|
|
317
|
+
limit: number = 10,
|
|
318
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
319
|
+
): Promise<unknown> {
|
|
320
|
+
const url = `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/notice-management/products/?skip=${skip}&limit=${limit}`;
|
|
321
|
+
return apiFetch<unknown>(url, {
|
|
322
|
+
...options,
|
|
323
|
+
getAuthToken: async () => baseRequest.authToken,
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get user consents (privacy center)
|
|
329
|
+
*/
|
|
330
|
+
async getUserConsents(
|
|
331
|
+
baseRequest: BaseRequest,
|
|
332
|
+
contact: string,
|
|
333
|
+
offset?: number,
|
|
334
|
+
limit?: number,
|
|
335
|
+
search?: string,
|
|
336
|
+
status?: string,
|
|
337
|
+
product_uuid?: string,
|
|
338
|
+
language?: string,
|
|
339
|
+
authToken?: string,
|
|
340
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
341
|
+
): Promise<unknown> {
|
|
342
|
+
const queryParams = new URLSearchParams();
|
|
343
|
+
if (offset !== undefined) queryParams.append("offset", offset.toString());
|
|
344
|
+
if (limit !== undefined) queryParams.append("limit", limit.toString());
|
|
345
|
+
if (search) queryParams.append("search", search);
|
|
346
|
+
if (status) queryParams.append("status", status);
|
|
347
|
+
if (product_uuid) queryParams.append("product_uuid", product_uuid);
|
|
348
|
+
if (language) queryParams.append("language", language);
|
|
349
|
+
const queryString = queryParams.toString();
|
|
350
|
+
const url = privacyCenterUrl(baseRequest, `/user-consents${queryString ? `?${queryString}` : ""}`);
|
|
351
|
+
return privacyCenterApiFetch(url, {
|
|
352
|
+
...options,
|
|
353
|
+
method: "POST",
|
|
354
|
+
body: JSON.stringify({ contact }),
|
|
355
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Manage consent (privacy center) - revoke, regrant, or renew
|
|
361
|
+
*/
|
|
362
|
+
async manageConsent(
|
|
363
|
+
baseRequest: BaseRequest,
|
|
364
|
+
purposeId: string,
|
|
365
|
+
contact: string,
|
|
366
|
+
status: 'revoke' | 'regrant' | 'renew',
|
|
367
|
+
authToken?: string,
|
|
368
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">,
|
|
369
|
+
nominatorContact?: string,
|
|
370
|
+
dataElementUuids?: string[]
|
|
371
|
+
): Promise<unknown> {
|
|
372
|
+
const url = privacyCenterUrl(baseRequest, `/manage-consent?status=${status}`);
|
|
373
|
+
const body: Record<string, unknown> = { purpose_uuid: purposeId, contact };
|
|
374
|
+
if (nominatorContact) {
|
|
375
|
+
body.nominator_contact = nominatorContact;
|
|
376
|
+
}
|
|
377
|
+
if (Array.isArray(dataElementUuids) && dataElementUuids.length > 0) {
|
|
378
|
+
body.data_element_uuids = dataElementUuids;
|
|
379
|
+
}
|
|
380
|
+
return privacyCenterApiFetch(url, {
|
|
381
|
+
...options,
|
|
382
|
+
method: "POST",
|
|
383
|
+
body: JSON.stringify(body),
|
|
384
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
385
|
+
});
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get activities (privacy center)
|
|
390
|
+
*/
|
|
391
|
+
async getActivities(
|
|
392
|
+
baseRequest: BaseRequest,
|
|
393
|
+
offset?: number,
|
|
394
|
+
limit?: number,
|
|
395
|
+
language?: string,
|
|
396
|
+
authToken?: string,
|
|
397
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
398
|
+
): Promise<unknown> {
|
|
399
|
+
const queryParams = new URLSearchParams();
|
|
400
|
+
if (offset !== undefined) queryParams.append("offset", offset.toString());
|
|
401
|
+
if (limit !== undefined) queryParams.append("limit", limit.toString());
|
|
402
|
+
if (language) queryParams.append("language", language);
|
|
403
|
+
const queryString = queryParams.toString();
|
|
404
|
+
const url = privacyCenterUrl(baseRequest, `/activities${queryString ? `?${queryString}` : ""}`);
|
|
405
|
+
return privacyCenterApiFetch<unknown>(url, {
|
|
406
|
+
...options,
|
|
407
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
408
|
+
});
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Get case history (privacy center)
|
|
413
|
+
*/
|
|
414
|
+
async getCaseHistory(
|
|
415
|
+
baseRequest: BaseRequest,
|
|
416
|
+
_contact: string,
|
|
417
|
+
offset?: number,
|
|
418
|
+
limit?: number,
|
|
419
|
+
language?: string,
|
|
420
|
+
authToken?: string,
|
|
421
|
+
options?: Omit<ApiFetchOptions, "getAuthToken">
|
|
422
|
+
): Promise<unknown> {
|
|
423
|
+
const queryParams = new URLSearchParams();
|
|
424
|
+
if (offset !== undefined) queryParams.append("offset", offset.toString());
|
|
425
|
+
if (limit !== undefined) queryParams.append("limit", limit.toString());
|
|
426
|
+
if (language) queryParams.append("language", language);
|
|
427
|
+
const queryString = queryParams.toString();
|
|
428
|
+
const url = privacyCenterUrl(baseRequest, `/cases?${queryString}`);
|
|
429
|
+
return privacyCenterApiFetch<unknown>(url, {
|
|
430
|
+
...options,
|
|
431
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
// ─── Case Communication ─────────────────────────────────────────────────
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Get messages for a case (privacy center)
|
|
439
|
+
*/
|
|
440
|
+
async getCaseMessages(
|
|
441
|
+
baseRequest: BaseRequest,
|
|
442
|
+
caseUuid: string,
|
|
443
|
+
authToken?: string
|
|
444
|
+
): Promise<CaseMessage[]> {
|
|
445
|
+
const url = caseUrl(baseRequest, caseUuid, '/messages');
|
|
446
|
+
return privacyCenterApiFetch<CaseMessage[]>(url, {
|
|
447
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Send a message on a case (privacy center)
|
|
453
|
+
*/
|
|
454
|
+
async sendCaseMessage(
|
|
455
|
+
baseRequest: BaseRequest,
|
|
456
|
+
caseUuid: string,
|
|
457
|
+
body: string,
|
|
458
|
+
documentUuids: string[],
|
|
459
|
+
authToken?: string
|
|
460
|
+
): Promise<CaseMessage> {
|
|
461
|
+
const url = caseUrl(baseRequest, caseUuid, '/messages');
|
|
462
|
+
return privacyCenterApiFetch<CaseMessage>(url, {
|
|
463
|
+
method: 'POST',
|
|
464
|
+
body: JSON.stringify({ body, document_uuids: documentUuids }),
|
|
465
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Upload a document on a case (multipart/form-data, privacy center)
|
|
471
|
+
*/
|
|
472
|
+
async uploadCaseDocument(
|
|
473
|
+
baseRequest: BaseRequest,
|
|
474
|
+
caseUuid: string,
|
|
475
|
+
file: File,
|
|
476
|
+
authToken?: string
|
|
477
|
+
): Promise<UploadDocumentResponse> {
|
|
478
|
+
const url = caseUrl(baseRequest, caseUuid, '/upload-document');
|
|
479
|
+
const formData = new FormData();
|
|
480
|
+
formData.append('file', file, file.name); // field name must be exactly "file"
|
|
481
|
+
// "payload" is required by the privacy-center endpoint (omitting it causes 422)
|
|
482
|
+
formData.append('payload', JSON.stringify({ metadata: null }));
|
|
483
|
+
return privacyCenterApiFetch<UploadDocumentResponse>(url, {
|
|
484
|
+
method: 'POST',
|
|
485
|
+
body: formData,
|
|
486
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
487
|
+
});
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Submit a document against a handler document request (privacy center)
|
|
492
|
+
*/
|
|
493
|
+
async submitDocumentForRequest(
|
|
494
|
+
baseRequest: BaseRequest,
|
|
495
|
+
caseUuid: string,
|
|
496
|
+
requestEventUuid: string,
|
|
497
|
+
documentUuid: string,
|
|
498
|
+
body: string,
|
|
499
|
+
authToken?: string
|
|
500
|
+
): Promise<CaseMessage> {
|
|
501
|
+
const url = caseUrl(baseRequest, caseUuid, `/document-requests/${requestEventUuid}/submit`);
|
|
502
|
+
return privacyCenterApiFetch<CaseMessage>(url, {
|
|
503
|
+
method: 'POST',
|
|
504
|
+
body: JSON.stringify({ document_uuid: documentUuid, body }),
|
|
505
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
506
|
+
});
|
|
507
|
+
},
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get document info/download URL for a case document (privacy center)
|
|
511
|
+
*/
|
|
512
|
+
async getCaseDocument(
|
|
513
|
+
baseRequest: BaseRequest,
|
|
514
|
+
caseUuid: string,
|
|
515
|
+
documentUuid: string,
|
|
516
|
+
authToken?: string
|
|
517
|
+
): Promise<MessageDocument> {
|
|
518
|
+
const url = caseUrl(baseRequest, caseUuid, `/documents/${documentUuid}`);
|
|
519
|
+
return privacyCenterApiFetch<MessageDocument>(url, {
|
|
520
|
+
authToken: resolveAuthToken(authToken, baseRequest),
|
|
521
|
+
});
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side configuration for backend service URLs.
|
|
3
|
+
* consent.baseUrl is set via BaseUrlProvider context.
|
|
4
|
+
*/
|
|
5
|
+
let baseUrl: string | undefined = undefined;
|
|
6
|
+
|
|
7
|
+
export const config = {
|
|
8
|
+
consent: {
|
|
9
|
+
get baseUrl() {
|
|
10
|
+
return baseUrl;
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Sets the base URL from the context provider.
|
|
17
|
+
* This should be called by BaseUrlProvider when it mounts.
|
|
18
|
+
*/
|
|
19
|
+
export function setUserBaseUrl(url: string): void {
|
|
20
|
+
baseUrl = url;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validates that required base URL is set.
|
|
25
|
+
* Throws an error if base URL is missing.
|
|
26
|
+
*/
|
|
27
|
+
export function validateConfig(): void {
|
|
28
|
+
if (!config.consent.baseUrl) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"Base URL is not defined. Ensure BaseUrlProvider is set up correctly."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|