@redacto.io/consent-sdk-react-native 5.0.0-beta.1 → 5.0.0-beta.10
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/README.md +75 -0
- package/dist/chunk-B2TXWC2C.mjs +60 -0
- package/dist/index.d.mts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +196 -55
- package/dist/index.mjs +153 -56
- package/dist/privacy-center.d.mts +147 -63
- package/dist/privacy-center.d.ts +147 -63
- package/dist/privacy-center.js +1499 -657
- package/dist/privacy-center.mjs +1459 -654
- package/package.json +1 -1
- package/dist/chunk-Y6FXYEAI.mjs +0 -10
package/README.md
CHANGED
|
@@ -64,6 +64,81 @@ pnpm add @tanstack/react-query i18next react-i18next dayjs \
|
|
|
64
64
|
|
|
65
65
|
> **Note:** `react-native-svg` must be linked for native builds. Follow the [react-native-svg setup guide](https://github.com/software-mansion/react-native-svg#installation).
|
|
66
66
|
|
|
67
|
+
## Sandbox Mode (No Backend Required)
|
|
68
|
+
|
|
69
|
+
Sandbox mode lets you build and test a full consent PoC — **notice → Privacy Center → DSAR** — straight from your app, with **no backend and no token minting**. Instead of your server generating a JWT, you paste a **static sandbox token** into the SDK. Everything you create is recorded as test data, isolated from your live records.
|
|
70
|
+
|
|
71
|
+
Going live is a config swap, not a rewrite: drop the sandbox props and pass a real JWT (see [Backend Server Setup](#backend-server-setup)).
|
|
72
|
+
|
|
73
|
+
### 1. Get your sandbox credentials
|
|
74
|
+
|
|
75
|
+
In the **Redacto Dashboard → API Keys → Sandbox** tab, copy:
|
|
76
|
+
|
|
77
|
+
- your **Sandbox token** (a static string; it never expires and needs no refresh)
|
|
78
|
+
- your **Organization ID** and **Workspace ID** (shown under *Workspace identifiers* on the same page)
|
|
79
|
+
|
|
80
|
+
### 2. Collect consent
|
|
81
|
+
|
|
82
|
+
Pass the sandbox `token`, org/workspace, and a test identity (`email`, `mobile`, or `ucic`). No `accessToken` is needed:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { RedactoNoticeConsent } from "@redacto.io/consent-sdk-react-native";
|
|
86
|
+
|
|
87
|
+
<RedactoNoticeConsent
|
|
88
|
+
noticeId={NOTICE_UUID}
|
|
89
|
+
token={SANDBOX_TOKEN}
|
|
90
|
+
organisationUuid={ORG_UUID}
|
|
91
|
+
workspaceUuid={WORKSPACE_UUID}
|
|
92
|
+
email="test.user@example.com" // or `mobile`, or `ucic`
|
|
93
|
+
onAccept={() => console.log("consent recorded (test)")}
|
|
94
|
+
onDecline={() => {}}
|
|
95
|
+
/>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Manage consent & raise a DSAR
|
|
99
|
+
|
|
100
|
+
Point the Privacy Center at the same sandbox token and test identity. The same test user can now review the consent from step 2, withdraw it, and file a DSAR — end to end, all as test data:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { RedactoPrivacyCenter } from "@redacto.io/consent-sdk-react-native/privacy-center";
|
|
104
|
+
|
|
105
|
+
<RedactoPrivacyCenter
|
|
106
|
+
baseUrl={BASE_URL}
|
|
107
|
+
slug="acme-co"
|
|
108
|
+
token={SANDBOX_TOKEN}
|
|
109
|
+
organisationUuid={ORG_UUID}
|
|
110
|
+
workspaceUuid={WORKSPACE_UUID}
|
|
111
|
+
email="test.user@example.com" // or `mobile`, or `ucic`
|
|
112
|
+
onError={(error) => {
|
|
113
|
+
console.error(error);
|
|
114
|
+
return false;
|
|
115
|
+
}}
|
|
116
|
+
/>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Sandbox props
|
|
120
|
+
|
|
121
|
+
Both `RedactoNoticeConsent` and `RedactoPrivacyCenter` accept the same sandbox props:
|
|
122
|
+
|
|
123
|
+
| Prop | Required | Description |
|
|
124
|
+
| --------------------------- | ------------ | ----------------------------------------------------------------------------------- |
|
|
125
|
+
| `token` | Yes | Static sandbox token from the Dashboard. Its presence activates sandbox mode. |
|
|
126
|
+
| `organisationUuid` | Yes | Organization ID from the Dashboard. |
|
|
127
|
+
| `workspaceUuid` | Yes | Workspace ID from the Dashboard. |
|
|
128
|
+
| `email` / `mobile` / `ucic` | One required | The test subject. Precedence when more than one is set: `ucic` > `email` > `mobile`. |
|
|
129
|
+
|
|
130
|
+
### How it works
|
|
131
|
+
|
|
132
|
+
- The SDK sends the sandbox token as an `X-Consent-Token` header and the test identity inside each request (query params on reads, body on writes) — no JWT decode, no token refresh (a `401` fails fast).
|
|
133
|
+
- The server records everything under `environment=test`, namespaced so sandbox data never mixes with — or reads — your live records.
|
|
134
|
+
- Sandbox always talks to the consent server; it never routes to the Go ledger.
|
|
135
|
+
|
|
136
|
+
### Going live
|
|
137
|
+
|
|
138
|
+
Sandbox and production use the same components. To ship, remove the sandbox props and pass a real `accessToken` / `refreshToken` (JWT) minted by your backend — see [Backend Server Setup](#backend-server-setup) below. New records are then written as `environment=live`.
|
|
139
|
+
|
|
140
|
+
> Sandbox is available on `RedactoNoticeConsent` and `RedactoPrivacyCenter`. The inline widget (`RedactoNoticeConsentInline`) is JWT-only.
|
|
141
|
+
|
|
67
142
|
## Backend Server Setup
|
|
68
143
|
|
|
69
144
|
The SDK never talks to Redacto with your CMS API key directly — your backend acts as a token broker. Obtain your **CMS-API-Key** from the Redacto Dashboard, store it in your backend's environment, and expose two endpoints to your mobile app:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/shared/languages.ts
|
|
9
|
+
var languageOptions = [
|
|
10
|
+
{ value: "en", label: "English" },
|
|
11
|
+
{ value: "hi", label: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)" },
|
|
12
|
+
{ value: "te", label: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)" },
|
|
13
|
+
{ value: "mr", label: "\u092E\u0930\u093E\u0920\u0940 (Marathi)" },
|
|
14
|
+
{ value: "ta", label: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)" },
|
|
15
|
+
{ value: "ur", label: "\u0627\u0631\u062F\u0648 (Urdu)" },
|
|
16
|
+
{ value: "gu", label: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)" },
|
|
17
|
+
{ value: "kn", label: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)" },
|
|
18
|
+
{ value: "ml", label: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)" },
|
|
19
|
+
{ value: "pa", label: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)" },
|
|
20
|
+
{ value: "or", label: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)" },
|
|
21
|
+
{ value: "as", label: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)" },
|
|
22
|
+
{ value: "bn", label: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)" },
|
|
23
|
+
{ value: "mai", label: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)" },
|
|
24
|
+
{ value: "ne", label: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)" },
|
|
25
|
+
{ value: "sd", label: "\u0633\u0646\u068C\u064A (Sindhi)" },
|
|
26
|
+
{ value: "doi", label: "\u0921\u094B\u0917\u0930\u0940 (Dogri)" },
|
|
27
|
+
{ value: "mni-Mtei", label: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)" },
|
|
28
|
+
{ value: "gom", label: "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)" },
|
|
29
|
+
{ value: "sa", label: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)" },
|
|
30
|
+
{ value: "brx", label: "\u092C\u0921\u093C\u094B (Bodo)" },
|
|
31
|
+
{ value: "ks", label: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)" },
|
|
32
|
+
{ value: "sat", label: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)" }
|
|
33
|
+
];
|
|
34
|
+
var codeByLabel = new Map(languageOptions.map((o) => [o.label, o.value]));
|
|
35
|
+
var codeByLower = new Map(languageOptions.map((o) => [o.value.toLowerCase(), o.value]));
|
|
36
|
+
var englishName = (label) => {
|
|
37
|
+
var _a, _b;
|
|
38
|
+
return (_b = (_a = label.match(/\(([^)]+)\)\s*$/)) == null ? void 0 : _a[1]) != null ? _b : label;
|
|
39
|
+
};
|
|
40
|
+
var codeByName = new Map(
|
|
41
|
+
languageOptions.map((o) => [englishName(o.label).toLowerCase(), o.value])
|
|
42
|
+
);
|
|
43
|
+
function toLanguageCode(lang) {
|
|
44
|
+
var _a, _b, _c;
|
|
45
|
+
if (!lang) return void 0;
|
|
46
|
+
const trimmed = lang.trim();
|
|
47
|
+
if (!trimmed) return void 0;
|
|
48
|
+
const lower = trimmed.toLowerCase();
|
|
49
|
+
return (_c = (_b = (_a = codeByLabel.get(trimmed)) != null ? _a : codeByName.get(lower)) != null ? _b : codeByLower.get(lower)) != null ? _c : trimmed;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/shared/sandbox.ts
|
|
53
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
__require,
|
|
57
|
+
SANDBOX_TOKEN_HEADER,
|
|
58
|
+
languageOptions,
|
|
59
|
+
toLanguageCode
|
|
60
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -28,8 +28,34 @@ type Settings$1 = {
|
|
|
28
28
|
|
|
29
29
|
type Props$1 = Readonly<{
|
|
30
30
|
noticeId: string;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* JWT access token. Optional so sandbox integrations (see `token`) can
|
|
33
|
+
* omit it. Required in the normal JWT flow.
|
|
34
|
+
*/
|
|
35
|
+
accessToken?: string;
|
|
36
|
+
/** JWT refresh token. Optional; accepted for source-compat, currently unused. */
|
|
37
|
+
refreshToken?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Sandbox PoC: a pasted static token. When set, the SDK runs against the
|
|
40
|
+
* sandbox with no JWT — it sends the single `X-Consent-Token` header, carries
|
|
41
|
+
* the acting identity (`email` || `mobile` || `ucic`) in each request's own
|
|
42
|
+
* payload, takes org/workspace from `organisationUuid`/`workspaceUuid`, and
|
|
43
|
+
* does no token refresh.
|
|
44
|
+
*/
|
|
45
|
+
token?: string;
|
|
46
|
+
/** Sandbox test subject email (carried in the request payload when set). */
|
|
47
|
+
email?: string;
|
|
48
|
+
/** Sandbox test subject mobile (fallback subject when `email` is absent). */
|
|
49
|
+
mobile?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
|
|
52
|
+
* customer code; optional third identifier alongside email/mobile.
|
|
53
|
+
*/
|
|
54
|
+
ucic?: string;
|
|
55
|
+
/** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
|
|
56
|
+
organisationUuid?: string;
|
|
57
|
+
/** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
|
|
58
|
+
workspaceUuid?: string;
|
|
33
59
|
baseUrl?: string;
|
|
34
60
|
/** Go ledger base URL — serves check-consent and the consent write; omit to use the legacy consent server. */
|
|
35
61
|
ledgerBaseUrl?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -28,8 +28,34 @@ type Settings$1 = {
|
|
|
28
28
|
|
|
29
29
|
type Props$1 = Readonly<{
|
|
30
30
|
noticeId: string;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* JWT access token. Optional so sandbox integrations (see `token`) can
|
|
33
|
+
* omit it. Required in the normal JWT flow.
|
|
34
|
+
*/
|
|
35
|
+
accessToken?: string;
|
|
36
|
+
/** JWT refresh token. Optional; accepted for source-compat, currently unused. */
|
|
37
|
+
refreshToken?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Sandbox PoC: a pasted static token. When set, the SDK runs against the
|
|
40
|
+
* sandbox with no JWT — it sends the single `X-Consent-Token` header, carries
|
|
41
|
+
* the acting identity (`email` || `mobile` || `ucic`) in each request's own
|
|
42
|
+
* payload, takes org/workspace from `organisationUuid`/`workspaceUuid`, and
|
|
43
|
+
* does no token refresh.
|
|
44
|
+
*/
|
|
45
|
+
token?: string;
|
|
46
|
+
/** Sandbox test subject email (carried in the request payload when set). */
|
|
47
|
+
email?: string;
|
|
48
|
+
/** Sandbox test subject mobile (fallback subject when `email` is absent). */
|
|
49
|
+
mobile?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Sandbox UCIC: the client's own user id / org_user_id, e.g. a UUID or
|
|
52
|
+
* customer code; optional third identifier alongside email/mobile.
|
|
53
|
+
*/
|
|
54
|
+
ucic?: string;
|
|
55
|
+
/** Sandbox org UUID (replaces the JWT-decoded org in sandbox mode). */
|
|
56
|
+
organisationUuid?: string;
|
|
57
|
+
/** Sandbox workspace UUID (replaces the JWT-decoded workspace in sandbox mode). */
|
|
58
|
+
workspaceUuid?: string;
|
|
33
59
|
baseUrl?: string;
|
|
34
60
|
/** Go ledger base URL — serves check-consent and the consent write; omit to use the legacy consent server. */
|
|
35
61
|
ledgerBaseUrl?: string;
|
package/dist/index.js
CHANGED
|
@@ -43,9 +43,114 @@ var import_react_native_svg3 = __toESM(require("react-native-svg"));
|
|
|
43
43
|
// src/RedactoNoticeConsent/api/index.ts
|
|
44
44
|
var import_jwt_decode = require("jwt-decode");
|
|
45
45
|
|
|
46
|
+
// src/shared/sandbox.ts
|
|
47
|
+
var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
|
|
48
|
+
|
|
49
|
+
// src/RedactoNoticeConsent/api/sandbox.ts
|
|
50
|
+
var resolveSandboxAuth = (params) => {
|
|
51
|
+
var _a, _b, _c, _d, _e;
|
|
52
|
+
const token = (_a = params.token) == null ? void 0 : _a.trim();
|
|
53
|
+
if (!token) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const organisationUuid = (_b = params.organisationUuid) == null ? void 0 : _b.trim();
|
|
57
|
+
const workspaceUuid = (_c = params.workspaceUuid) == null ? void 0 : _c.trim();
|
|
58
|
+
const ucic = (_d = params.sandboxUcic) == null ? void 0 : _d.trim();
|
|
59
|
+
const subject = (_e = params.sandboxSubject) == null ? void 0 : _e.trim();
|
|
60
|
+
if (!organisationUuid || !workspaceUuid) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Sandbox mode requires organisationUuid and workspaceUuid props"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (!ucic && !subject) {
|
|
66
|
+
throw new Error("Sandbox mode requires a UCIC, email, or mobile");
|
|
67
|
+
}
|
|
68
|
+
return { token, ucic, subject: subject != null ? subject : "", organisationUuid, workspaceUuid };
|
|
69
|
+
};
|
|
70
|
+
var resolveRequestAuth = (accessToken, sandboxParams, decodeTokenSafely2) => {
|
|
71
|
+
const sandbox = resolveSandboxAuth(sandboxParams);
|
|
72
|
+
if (sandbox) {
|
|
73
|
+
return {
|
|
74
|
+
organisationUuid: sandbox.organisationUuid,
|
|
75
|
+
workspaceUuid: sandbox.workspaceUuid,
|
|
76
|
+
sandbox
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const decodedToken = accessToken ? decodeTokenSafely2(accessToken) : null;
|
|
80
|
+
if (!decodedToken) {
|
|
81
|
+
throw new Error("Invalid access token");
|
|
82
|
+
}
|
|
83
|
+
const { organisation_uuid, workspace_uuid } = decodedToken;
|
|
84
|
+
if (!organisation_uuid || !workspace_uuid) {
|
|
85
|
+
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
organisationUuid: organisation_uuid,
|
|
89
|
+
workspaceUuid: workspace_uuid,
|
|
90
|
+
sandbox: null
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
var buildAuthHeaders = (accessToken, sandbox) => {
|
|
94
|
+
if (sandbox) {
|
|
95
|
+
return { [SANDBOX_TOKEN_HEADER]: sandbox.token };
|
|
96
|
+
}
|
|
97
|
+
return { Authorization: `Bearer ${accessToken}` };
|
|
98
|
+
};
|
|
99
|
+
var sandboxBodyIdentity = (sandbox) => {
|
|
100
|
+
if (sandbox.ucic) {
|
|
101
|
+
return { org_user_id: sandbox.ucic };
|
|
102
|
+
}
|
|
103
|
+
return sandbox.subject.includes("@") ? { primary_email: sandbox.subject } : { primary_mobile: sandbox.subject };
|
|
104
|
+
};
|
|
105
|
+
var sandboxQueryIdentity = (sandbox) => sandboxBodyIdentity(sandbox);
|
|
106
|
+
|
|
46
107
|
// src/constants.ts
|
|
47
108
|
var DEFAULT_CONSENT_API_BASE_URL = "https://api.redacto.io/consent";
|
|
48
109
|
|
|
110
|
+
// src/shared/languages.ts
|
|
111
|
+
var languageOptions = [
|
|
112
|
+
{ value: "en", label: "English" },
|
|
113
|
+
{ value: "hi", label: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)" },
|
|
114
|
+
{ value: "te", label: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)" },
|
|
115
|
+
{ value: "mr", label: "\u092E\u0930\u093E\u0920\u0940 (Marathi)" },
|
|
116
|
+
{ value: "ta", label: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)" },
|
|
117
|
+
{ value: "ur", label: "\u0627\u0631\u062F\u0648 (Urdu)" },
|
|
118
|
+
{ value: "gu", label: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)" },
|
|
119
|
+
{ value: "kn", label: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)" },
|
|
120
|
+
{ value: "ml", label: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)" },
|
|
121
|
+
{ value: "pa", label: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)" },
|
|
122
|
+
{ value: "or", label: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)" },
|
|
123
|
+
{ value: "as", label: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)" },
|
|
124
|
+
{ value: "bn", label: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)" },
|
|
125
|
+
{ value: "mai", label: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)" },
|
|
126
|
+
{ value: "ne", label: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)" },
|
|
127
|
+
{ value: "sd", label: "\u0633\u0646\u068C\u064A (Sindhi)" },
|
|
128
|
+
{ value: "doi", label: "\u0921\u094B\u0917\u0930\u0940 (Dogri)" },
|
|
129
|
+
{ value: "mni-Mtei", label: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)" },
|
|
130
|
+
{ value: "gom", label: "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)" },
|
|
131
|
+
{ value: "sa", label: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)" },
|
|
132
|
+
{ value: "brx", label: "\u092C\u0921\u093C\u094B (Bodo)" },
|
|
133
|
+
{ value: "ks", label: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)" },
|
|
134
|
+
{ value: "sat", label: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)" }
|
|
135
|
+
];
|
|
136
|
+
var codeByLabel = new Map(languageOptions.map((o) => [o.label, o.value]));
|
|
137
|
+
var codeByLower = new Map(languageOptions.map((o) => [o.value.toLowerCase(), o.value]));
|
|
138
|
+
var englishName = (label) => {
|
|
139
|
+
var _a, _b;
|
|
140
|
+
return (_b = (_a = label.match(/\(([^)]+)\)\s*$/)) == null ? void 0 : _a[1]) != null ? _b : label;
|
|
141
|
+
};
|
|
142
|
+
var codeByName = new Map(
|
|
143
|
+
languageOptions.map((o) => [englishName(o.label).toLowerCase(), o.value])
|
|
144
|
+
);
|
|
145
|
+
function toLanguageCode(lang) {
|
|
146
|
+
var _a, _b, _c;
|
|
147
|
+
if (!lang) return void 0;
|
|
148
|
+
const trimmed = lang.trim();
|
|
149
|
+
if (!trimmed) return void 0;
|
|
150
|
+
const lower = trimmed.toLowerCase();
|
|
151
|
+
return (_c = (_b = (_a = codeByLabel.get(trimmed)) != null ? _a : codeByName.get(lower)) != null ? _b : codeByLower.get(lower)) != null ? _c : trimmed;
|
|
152
|
+
}
|
|
153
|
+
|
|
49
154
|
// src/RedactoNoticeConsent/api/index.ts
|
|
50
155
|
var apiCache = /* @__PURE__ */ new Map();
|
|
51
156
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -81,21 +186,19 @@ var fetchConsentContent = async ({
|
|
|
81
186
|
specific_uuid,
|
|
82
187
|
validate_against = "all",
|
|
83
188
|
include_fully_consented_data = false,
|
|
84
|
-
signal
|
|
189
|
+
signal,
|
|
190
|
+
...sandboxParams
|
|
85
191
|
}) => {
|
|
86
|
-
if (!noticeId || !
|
|
87
|
-
throw new Error("noticeId
|
|
88
|
-
}
|
|
89
|
-
const decodedToken = decodeTokenSafely(accessToken);
|
|
90
|
-
if (!decodedToken) {
|
|
91
|
-
throw new Error("Invalid access token");
|
|
192
|
+
if (!noticeId || !validate_against) {
|
|
193
|
+
throw new Error("noticeId is required");
|
|
92
194
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
195
|
+
if (!accessToken && !sandboxParams.token) {
|
|
196
|
+
throw new Error("accessToken or token is required");
|
|
96
197
|
}
|
|
198
|
+
const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
|
|
97
199
|
const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
|
|
98
|
-
const
|
|
200
|
+
const cacheIdentity = sandbox ? `sandbox:${sandbox.organisationUuid}:${sandbox.workspaceUuid}:${sandbox.ucic || sandbox.subject}` : accessToken;
|
|
201
|
+
const cacheKey = `${cacheIdentity}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
99
202
|
if (validate_against === "all" && !specific_uuid) {
|
|
100
203
|
const cachedData = getCachedData(cacheKey);
|
|
101
204
|
if (cachedData) {
|
|
@@ -112,10 +215,15 @@ var fetchConsentContent = async ({
|
|
|
112
215
|
if (include_fully_consented_data) {
|
|
113
216
|
url.searchParams.append("include_fully_consented_data", "true");
|
|
114
217
|
}
|
|
218
|
+
if (sandbox) {
|
|
219
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
220
|
+
url.searchParams.append(key, value);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
115
223
|
const response = await fetch(url.toString(), {
|
|
116
224
|
method: "GET",
|
|
117
225
|
headers: {
|
|
118
|
-
|
|
226
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
119
227
|
"Accept-Language": language,
|
|
120
228
|
"Content-Type": "application/json"
|
|
121
229
|
},
|
|
@@ -154,20 +262,18 @@ var submitConsentEvent = async ({
|
|
|
154
262
|
meta_data,
|
|
155
263
|
guardianVerificationReference,
|
|
156
264
|
selfDeclaredAdult,
|
|
157
|
-
|
|
265
|
+
language,
|
|
266
|
+
signal,
|
|
267
|
+
...sandboxParams
|
|
158
268
|
}) => {
|
|
159
|
-
if (!noticeUuid || !
|
|
160
|
-
throw new Error("noticeUuid
|
|
161
|
-
}
|
|
162
|
-
const decodedToken = decodeTokenSafely(accessToken);
|
|
163
|
-
if (!decodedToken) {
|
|
164
|
-
throw new Error("Invalid access token");
|
|
269
|
+
if (!noticeUuid || !Array.isArray(purposes)) {
|
|
270
|
+
throw new Error("noticeUuid and purposes array are required");
|
|
165
271
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
272
|
+
if (!accessToken && !sandboxParams.token) {
|
|
273
|
+
throw new Error("accessToken or token is required");
|
|
169
274
|
}
|
|
170
|
-
const
|
|
275
|
+
const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
|
|
276
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || DEFAULT_CONSENT_API_BASE_URL;
|
|
171
277
|
const validatedPurposes = purposes.map((purpose) => {
|
|
172
278
|
if (!purpose.uuid) {
|
|
173
279
|
throw new Error("Invalid purpose data: missing uuid");
|
|
@@ -190,17 +296,19 @@ var submitConsentEvent = async ({
|
|
|
190
296
|
declined: Boolean(declined),
|
|
191
297
|
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid } : void 0,
|
|
192
298
|
guardian_verification_reference: guardianVerificationReference,
|
|
193
|
-
self_declared_adult: selfDeclaredAdult
|
|
299
|
+
self_declared_adult: selfDeclaredAdult,
|
|
300
|
+
language: toLanguageCode(language)
|
|
194
301
|
};
|
|
302
|
+
const requestBody = sandbox ? { ...payload, ...sandboxBodyIdentity(sandbox) } : payload;
|
|
195
303
|
const response = await fetch(
|
|
196
304
|
`${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/submit-consent`,
|
|
197
305
|
{
|
|
198
306
|
method: "POST",
|
|
199
307
|
headers: {
|
|
200
|
-
|
|
308
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
201
309
|
"Content-Type": "application/json"
|
|
202
310
|
},
|
|
203
|
-
body: JSON.stringify(
|
|
311
|
+
body: JSON.stringify(requestBody),
|
|
204
312
|
signal
|
|
205
313
|
}
|
|
206
314
|
);
|
|
@@ -226,28 +334,30 @@ var submitConsentEvent = async ({
|
|
|
226
334
|
var fetchTTSAudioUrls = async ({
|
|
227
335
|
accessToken,
|
|
228
336
|
baseUrl,
|
|
337
|
+
ledgerBaseUrl,
|
|
229
338
|
noticeUuid,
|
|
230
339
|
language,
|
|
231
|
-
signal
|
|
340
|
+
signal,
|
|
341
|
+
...sandboxParams
|
|
232
342
|
}) => {
|
|
233
|
-
if (!accessToken || !noticeUuid || !language) {
|
|
234
|
-
throw new Error("accessToken, noticeUuid, and language are required");
|
|
235
|
-
}
|
|
236
|
-
const decodedToken = decodeTokenSafely(accessToken);
|
|
237
|
-
if (!decodedToken) {
|
|
238
|
-
throw new Error("Invalid access token");
|
|
239
|
-
}
|
|
240
|
-
const { organisation_uuid: ORGANISATION_UUID, workspace_uuid: WORKSPACE_UUID } = decodedToken;
|
|
241
|
-
if (!ORGANISATION_UUID || !WORKSPACE_UUID) {
|
|
242
|
-
throw new Error("Invalid token: missing organization or workspace UUID");
|
|
343
|
+
if (!accessToken && !sandboxParams.token || !noticeUuid || !language) {
|
|
344
|
+
throw new Error("accessToken (or token), noticeUuid, and language are required");
|
|
243
345
|
}
|
|
244
|
-
const
|
|
346
|
+
const { organisationUuid: ORGANISATION_UUID, workspaceUuid: WORKSPACE_UUID, sandbox } = resolveRequestAuth(accessToken, sandboxParams, decodeTokenSafely);
|
|
347
|
+
const apiBaseUrl = (sandbox ? null : ledgerBaseUrl) || baseUrl || DEFAULT_CONSENT_API_BASE_URL;
|
|
245
348
|
const normalizedLanguage = language === "en" || language === "EN" ? "English" : language;
|
|
246
|
-
const url =
|
|
247
|
-
|
|
349
|
+
const url = new URL(
|
|
350
|
+
`${apiBaseUrl}/public/organisations/${ORGANISATION_UUID}/workspaces/${WORKSPACE_UUID}/notices/${noticeUuid}/audio/${encodeURIComponent(normalizedLanguage)}`
|
|
351
|
+
);
|
|
352
|
+
if (sandbox) {
|
|
353
|
+
for (const [key, value] of Object.entries(sandboxQueryIdentity(sandbox))) {
|
|
354
|
+
url.searchParams.append(key, value);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const response = await fetch(url.toString(), {
|
|
248
358
|
method: "GET",
|
|
249
359
|
headers: {
|
|
250
|
-
|
|
360
|
+
...buildAuthHeaders(accessToken, sandbox),
|
|
251
361
|
Accept: "application/json",
|
|
252
362
|
"Content-Type": "application/json"
|
|
253
363
|
},
|
|
@@ -1754,6 +1864,12 @@ var RedactoNoticeConsent = ({
|
|
|
1754
1864
|
noticeId,
|
|
1755
1865
|
accessToken,
|
|
1756
1866
|
refreshToken: _refreshToken,
|
|
1867
|
+
token,
|
|
1868
|
+
email,
|
|
1869
|
+
mobile,
|
|
1870
|
+
ucic,
|
|
1871
|
+
organisationUuid,
|
|
1872
|
+
workspaceUuid,
|
|
1757
1873
|
baseUrl,
|
|
1758
1874
|
ledgerBaseUrl,
|
|
1759
1875
|
settings,
|
|
@@ -1769,6 +1885,16 @@ var RedactoNoticeConsent = ({
|
|
|
1769
1885
|
}) => {
|
|
1770
1886
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
|
|
1771
1887
|
const { isMobile } = useResponsive();
|
|
1888
|
+
const sandboxParams = (0, import_react2.useMemo)(
|
|
1889
|
+
() => ({
|
|
1890
|
+
token,
|
|
1891
|
+
sandboxUcic: ucic,
|
|
1892
|
+
sandboxSubject: email || mobile,
|
|
1893
|
+
organisationUuid,
|
|
1894
|
+
workspaceUuid
|
|
1895
|
+
}),
|
|
1896
|
+
[token, ucic, email, mobile, organisationUuid, workspaceUuid]
|
|
1897
|
+
);
|
|
1772
1898
|
const [content, setContent] = (0, import_react2.useState)(null);
|
|
1773
1899
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
1774
1900
|
const [isSubmitting, setIsSubmitting] = (0, import_react2.useState)(false);
|
|
@@ -1823,20 +1949,22 @@ var RedactoNoticeConsent = ({
|
|
|
1823
1949
|
setIsLoading(true);
|
|
1824
1950
|
setErrorMessage(null);
|
|
1825
1951
|
try {
|
|
1952
|
+
const noticeBaseUrl = ledgerBaseUrl && !applicationId && !token ? ledgerBaseUrl : baseUrl;
|
|
1826
1953
|
const data = await fetchConsentContent({
|
|
1827
1954
|
noticeId,
|
|
1828
1955
|
accessToken,
|
|
1829
|
-
baseUrl,
|
|
1956
|
+
baseUrl: noticeBaseUrl,
|
|
1830
1957
|
language: "English",
|
|
1831
1958
|
validate_against: validateAgainst,
|
|
1832
1959
|
include_fully_consented_data: includeFullyConsentedData,
|
|
1833
1960
|
specific_uuid: applicationId,
|
|
1834
|
-
signal: abortControllerRef.current.signal
|
|
1961
|
+
signal: abortControllerRef.current.signal,
|
|
1962
|
+
...sandboxParams
|
|
1835
1963
|
});
|
|
1836
1964
|
const activeConfig2 = data.detail.active_config;
|
|
1837
1965
|
let purposeSelections = data.detail.purpose_selections;
|
|
1838
1966
|
let reconsentRequired = data.detail.reconsent_required || false;
|
|
1839
|
-
if (ledgerBaseUrl && !applicationId) {
|
|
1967
|
+
if (accessToken && ledgerBaseUrl && !applicationId && !token) {
|
|
1840
1968
|
const ledgerStatus = await fetchNoticeConsentStatus({
|
|
1841
1969
|
accessToken,
|
|
1842
1970
|
ledgerBaseUrl,
|
|
@@ -1913,7 +2041,7 @@ var RedactoNoticeConsent = ({
|
|
|
1913
2041
|
} finally {
|
|
1914
2042
|
setIsLoading(false);
|
|
1915
2043
|
}
|
|
1916
|
-
}, [noticeId, accessToken, baseUrl, ledgerBaseUrl, validateAgainst, includeFullyConsentedData, applicationId, onAccept, onError]);
|
|
2044
|
+
}, [noticeId, accessToken, sandboxParams, token, baseUrl, ledgerBaseUrl, validateAgainst, includeFullyConsentedData, applicationId, onAccept, onError]);
|
|
1917
2045
|
(0, import_react2.useEffect)(() => {
|
|
1918
2046
|
hasBootstrappedLanguageRef.current = false;
|
|
1919
2047
|
}, [noticeId, accessToken]);
|
|
@@ -1934,8 +2062,10 @@ var RedactoNoticeConsent = ({
|
|
|
1934
2062
|
const data = await fetchTTSAudioUrls({
|
|
1935
2063
|
accessToken,
|
|
1936
2064
|
baseUrl,
|
|
2065
|
+
ledgerBaseUrl,
|
|
1937
2066
|
noticeUuid: content.detail.active_config.notice_uuid,
|
|
1938
|
-
language: selectedLanguage
|
|
2067
|
+
language: selectedLanguage,
|
|
2068
|
+
...sandboxParams
|
|
1939
2069
|
});
|
|
1940
2070
|
const urls = {
|
|
1941
2071
|
notice_text: data.detail.notice_text_audio_url,
|
|
@@ -1989,7 +2119,7 @@ var RedactoNoticeConsent = ({
|
|
|
1989
2119
|
setTtsAudioUrls({});
|
|
1990
2120
|
setIsTTSAvailable(false);
|
|
1991
2121
|
}
|
|
1992
|
-
}, [content, accessToken, baseUrl, selectedLanguage]);
|
|
2122
|
+
}, [content, accessToken, sandboxParams, baseUrl, ledgerBaseUrl, selectedLanguage]);
|
|
1993
2123
|
const clearPlaybackWatchers = (0, import_react2.useCallback)(() => {
|
|
1994
2124
|
if (playbackSubscriptionRef.current) {
|
|
1995
2125
|
playbackSubscriptionRef.current.remove();
|
|
@@ -2329,7 +2459,9 @@ var RedactoNoticeConsent = ({
|
|
|
2329
2459
|
declined: false,
|
|
2330
2460
|
meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
|
|
2331
2461
|
guardianVerificationReference: verificationReferenceRef.current || void 0,
|
|
2332
|
-
selfDeclaredAdult: selfDeclaredAdult || void 0
|
|
2462
|
+
selfDeclaredAdult: selfDeclaredAdult || void 0,
|
|
2463
|
+
language: selectedLanguage,
|
|
2464
|
+
...sandboxParams
|
|
2333
2465
|
});
|
|
2334
2466
|
await stopAudio();
|
|
2335
2467
|
clearApiCache();
|
|
@@ -2344,7 +2476,7 @@ var RedactoNoticeConsent = ({
|
|
|
2344
2476
|
} finally {
|
|
2345
2477
|
setIsSubmitting(false);
|
|
2346
2478
|
}
|
|
2347
|
-
}, [content, accessToken, baseUrl, ledgerBaseUrl, applicationId, selectedPurposes, selectedDataElements, stopAudio, onAccept, onError]);
|
|
2479
|
+
}, [content, accessToken, sandboxParams, baseUrl, ledgerBaseUrl, applicationId, selectedPurposes, selectedDataElements, selectedLanguage, selfDeclaredAdult, stopAudio, onAccept, onError]);
|
|
2348
2480
|
const handleDecline = (0, import_react2.useCallback)(() => {
|
|
2349
2481
|
stopAudio();
|
|
2350
2482
|
stopPolling();
|
|
@@ -2946,14 +3078,16 @@ var fetchConsentContent2 = async ({
|
|
|
2946
3078
|
org_uuid,
|
|
2947
3079
|
workspace_uuid,
|
|
2948
3080
|
notice_uuid,
|
|
3081
|
+
accessToken,
|
|
2949
3082
|
baseUrl,
|
|
2950
3083
|
language = "en",
|
|
2951
3084
|
specific_uuid,
|
|
2952
3085
|
signal
|
|
2953
3086
|
}) => {
|
|
2954
3087
|
const apiBaseUrl = baseUrl || DEFAULT_CONSENT_API_BASE_URL;
|
|
3088
|
+
const noticesUrl = `${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices`;
|
|
2955
3089
|
const url = new URL(
|
|
2956
|
-
`${
|
|
3090
|
+
accessToken ? `${noticesUrl}/${notice_uuid}` : `${noticesUrl}/get-notice/${notice_uuid}`
|
|
2957
3091
|
);
|
|
2958
3092
|
if (specific_uuid) {
|
|
2959
3093
|
url.searchParams.append("specific_uuid", specific_uuid);
|
|
@@ -2962,7 +3096,8 @@ var fetchConsentContent2 = async ({
|
|
|
2962
3096
|
method: "GET",
|
|
2963
3097
|
headers: {
|
|
2964
3098
|
"Content-Type": "application/json",
|
|
2965
|
-
"Accept-Language": language
|
|
3099
|
+
"Accept-Language": language,
|
|
3100
|
+
...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
|
|
2966
3101
|
},
|
|
2967
3102
|
signal
|
|
2968
3103
|
});
|
|
@@ -2996,7 +3131,8 @@ var submitConsentEvent2 = async ({
|
|
|
2996
3131
|
noticeUuid,
|
|
2997
3132
|
purposes,
|
|
2998
3133
|
declined,
|
|
2999
|
-
meta_data
|
|
3134
|
+
meta_data,
|
|
3135
|
+
language
|
|
3000
3136
|
}) => {
|
|
3001
3137
|
var _a, _b;
|
|
3002
3138
|
const apiBaseUrl = ledgerBaseUrl || baseUrl || DEFAULT_CONSENT_API_BASE_URL;
|
|
@@ -3019,7 +3155,8 @@ var submitConsentEvent2 = async ({
|
|
|
3019
3155
|
select_all_mandatory: false,
|
|
3020
3156
|
source: "MOBILE",
|
|
3021
3157
|
declined,
|
|
3022
|
-
meta_data
|
|
3158
|
+
meta_data,
|
|
3159
|
+
language: toLanguageCode(language)
|
|
3023
3160
|
};
|
|
3024
3161
|
const response = await fetch(
|
|
3025
3162
|
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
@@ -3401,11 +3538,13 @@ var RedactoConsentInlineComponent = ({
|
|
|
3401
3538
|
const fetchNotice = (0, import_react3.useCallback)(async (signal) => {
|
|
3402
3539
|
setIsLoading(true);
|
|
3403
3540
|
setFetchError(null);
|
|
3541
|
+
setHasAlreadyConsented(false);
|
|
3404
3542
|
try {
|
|
3405
3543
|
const consentContentData = await fetchConsentContent2({
|
|
3406
3544
|
org_uuid,
|
|
3407
3545
|
workspace_uuid,
|
|
3408
3546
|
notice_uuid,
|
|
3547
|
+
accessToken,
|
|
3409
3548
|
baseUrl,
|
|
3410
3549
|
language,
|
|
3411
3550
|
specific_uuid: applicationId,
|
|
@@ -3455,7 +3594,7 @@ var RedactoConsentInlineComponent = ({
|
|
|
3455
3594
|
setIsLoading(false);
|
|
3456
3595
|
}
|
|
3457
3596
|
}
|
|
3458
|
-
}, [org_uuid, workspace_uuid, notice_uuid, baseUrl, language, applicationId, onError]);
|
|
3597
|
+
}, [org_uuid, workspace_uuid, notice_uuid, accessToken, baseUrl, language, applicationId, onError]);
|
|
3459
3598
|
(0, import_react3.useEffect)(() => {
|
|
3460
3599
|
const controller = new AbortController();
|
|
3461
3600
|
void fetchNotice(controller.signal);
|
|
@@ -3567,7 +3706,8 @@ var RedactoConsentInlineComponent = ({
|
|
|
3567
3706
|
};
|
|
3568
3707
|
}),
|
|
3569
3708
|
declined: false,
|
|
3570
|
-
meta_data: applicationId ? { specific_uuid: applicationId } : void 0
|
|
3709
|
+
meta_data: applicationId ? { specific_uuid: applicationId } : void 0,
|
|
3710
|
+
language: selectedLanguage
|
|
3571
3711
|
});
|
|
3572
3712
|
onAccept == null ? void 0 : onAccept();
|
|
3573
3713
|
hasSubmittedRef.current = false;
|
|
@@ -3590,6 +3730,7 @@ var RedactoConsentInlineComponent = ({
|
|
|
3590
3730
|
selectedDataElements,
|
|
3591
3731
|
selectedPurposes,
|
|
3592
3732
|
applicationId,
|
|
3733
|
+
selectedLanguage,
|
|
3593
3734
|
onAccept
|
|
3594
3735
|
]
|
|
3595
3736
|
);
|