@redacto.io/consent-sdk-react-native 5.0.0-beta.9 → 5.0.1-beta.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.
@@ -10579,7 +10579,7 @@ function createConsentClient(baseUrl, ledgerBaseUrl) {
10579
10579
  * Submit a document for a document request (privacy center)
10580
10580
  */
10581
10581
  async submitDocumentForRequest(baseRequest, caseUuid, requestEventUuid, documentUuid, body, authToken, options) {
10582
- const url = `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/cases/${caseUuid}/document-requests/${requestEventUuid}/submit`;
10582
+ const url = `${baseUrl}/organisations/${baseRequest.organisationUuid}/workspaces/${baseRequest.workspaceUuid}/dsar/privacy-center/cases/${caseUuid}/document-requests/${requestEventUuid}/submit${sandboxIdentitySuffix()}`;
10583
10583
  return privacyCenterApiFetch(url, {
10584
10584
  ...options,
10585
10585
  method: "POST",
@@ -11859,7 +11859,11 @@ var resolveContactFromJwt = (decoded) => {
11859
11859
  }
11860
11860
  const userData = decoded.user_data || {};
11861
11861
  const candidate = decoded.contact || decoded.primary_email || userData.primary_email || userData.primary_mobile || userData.org_user_id;
11862
- return typeof candidate === "string" && candidate.trim() !== "" ? candidate : null;
11862
+ if (typeof candidate !== "string") {
11863
+ return null;
11864
+ }
11865
+ const contact = candidate.trim();
11866
+ return contact === "" ? null : contact;
11863
11867
  };
11864
11868
  var isJwtExpired = (token, bufferSeconds = 30) => {
11865
11869
  if (!token || typeof token !== "string") return true;
@@ -12001,6 +12005,23 @@ var formatDateTime = (timestamp) => {
12001
12005
  hours = hours % 12 || 12;
12002
12006
  return `${month} ${day}, ${year} at ${hours}:${minutes} ${ampm}`;
12003
12007
  };
12008
+ var extractCaseRef = (response) => {
12009
+ if (!response || typeof response !== "object") {
12010
+ return null;
12011
+ }
12012
+ const wrapper = response;
12013
+ const candidates = [wrapper.detail, response];
12014
+ for (const candidate of candidates) {
12015
+ if (!candidate || typeof candidate !== "object") {
12016
+ continue;
12017
+ }
12018
+ const { case_id: caseId, uuid } = candidate;
12019
+ if (caseId || uuid) {
12020
+ return { case_id: caseId != null ? caseId : "", uuid: uuid != null ? uuid : "" };
12021
+ }
12022
+ }
12023
+ return null;
12024
+ };
12004
12025
  var generateUuid = () => {
12005
12026
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
12006
12027
  const r = Math.random() * 16 | 0;
@@ -12020,9 +12041,10 @@ function extractJwtAuthData(token) {
12020
12041
  if (!decoded) {
12021
12042
  return { email: null, organizationId: null, workspaceId: null, organizationName: null };
12022
12043
  }
12023
- const contact = resolveContactFromJwt(decoded) || getStoredValue("email");
12044
+ const contact = resolveContactFromJwt(decoded);
12024
12045
  return {
12025
- email: typeof contact === "string" && contact.trim() !== "" ? contact : null,
12046
+ // `resolveContactFromJwt` already returns a trimmed, non-empty string or null.
12047
+ email: contact,
12026
12048
  organizationId: decoded.organisation_uuid || null,
12027
12049
  workspaceId: decoded.workspace_uuid || null,
12028
12050
  organizationName: decoded.organisation_name || null
@@ -12031,7 +12053,11 @@ function extractJwtAuthData(token) {
12031
12053
  function syncJwtDataToStore(token) {
12032
12054
  const authData = extractJwtAuthData(token);
12033
12055
  setStoredValue("access_token", token);
12034
- if (authData.email) setStoredValue("email", authData.email);
12056
+ if (authData.email) {
12057
+ setStoredValue("email", authData.email);
12058
+ } else {
12059
+ removeStoredValue("email");
12060
+ }
12035
12061
  if (authData.organizationId) setStoredValue("pc_org_id", authData.organizationId);
12036
12062
  if (authData.workspaceId) setStoredValue("pc_workspace_id", authData.workspaceId);
12037
12063
  return authData;
@@ -14728,6 +14754,11 @@ var useTimePeriodOptions = () => {
14728
14754
  );
14729
14755
  };
14730
14756
  var MAX_DISPLAYED_PURPOSE_NAMES_IN_ERASURE_MODAL = 3;
14757
+ var PRODUCT_SCOPED_REQUEST_TYPES = [
14758
+ "ACCESS" /* ACCESS */,
14759
+ "erasure" /* ERASURE */,
14760
+ "CORRECTION" /* CORRECTION */
14761
+ ];
14731
14762
  var SEARCH_DEBOUNCE_DELAY = 500;
14732
14763
  var FORM_PICKER_GROUP_LIMIT = 100;
14733
14764
  var LEGACY_PRODUCT_GROUP_UUID = "__legacy_flat_purposes__";
@@ -16524,27 +16555,38 @@ var uploadFileWithS2S = async ({
16524
16555
  authToken,
16525
16556
  baseUrl
16526
16557
  }) => {
16558
+ var _a3;
16527
16559
  try {
16528
- if (!authToken) {
16529
- throw new Error("Auth token is required for document upload");
16530
- }
16531
16560
  if (!baseUrl) {
16532
16561
  throw new Error("Base URL is required for document upload");
16533
16562
  }
16534
- const decodedToken = decodeJwtToken(authToken);
16535
- if (!decodedToken) {
16536
- throw new Error("Invalid access token");
16537
- }
16538
- const { organisation_uuid, workspace_uuid } = decodedToken;
16539
- if (!organisation_uuid || !workspace_uuid) {
16540
- throw new Error("Invalid token: missing organization or workspace UUID");
16563
+ const sandbox = getSandboxConfig();
16564
+ let organisation_uuid;
16565
+ let workspace_uuid;
16566
+ let authHeaders;
16567
+ if (sandbox) {
16568
+ organisation_uuid = sandbox.organisationUuid;
16569
+ workspace_uuid = sandbox.workspaceUuid;
16570
+ authHeaders = (_a3 = sandboxAuthHeaders()) != null ? _a3 : {};
16571
+ } else {
16572
+ if (!authToken) {
16573
+ throw new Error("Auth token is required for document upload");
16574
+ }
16575
+ const decodedToken = decodeJwtToken(authToken);
16576
+ if (!decodedToken) {
16577
+ throw new Error("Invalid access token");
16578
+ }
16579
+ if (!decodedToken.organisation_uuid || !decodedToken.workspace_uuid) {
16580
+ throw new Error("Invalid token: missing organization or workspace UUID");
16581
+ }
16582
+ organisation_uuid = decodedToken.organisation_uuid;
16583
+ workspace_uuid = decodedToken.workspace_uuid;
16584
+ authHeaders = { Authorization: `Bearer ${authToken}` };
16541
16585
  }
16542
16586
  const url = `${baseUrl}/organisations/${organisation_uuid}/workspaces/${workspace_uuid}/dsar/privacy-center/upload-document`;
16543
16587
  const response = await fetch(url, {
16544
16588
  method: "POST",
16545
- headers: {
16546
- "Authorization": `Bearer ${authToken}`
16547
- },
16589
+ headers: authHeaders,
16548
16590
  body: formData
16549
16591
  });
16550
16592
  if (!response.ok) {
@@ -16571,6 +16613,25 @@ var uploadFileWithS2S = async ({
16571
16613
  }
16572
16614
  };
16573
16615
 
16616
+ // src/RedactoPrivacyCenter/lib/requestType.ts
16617
+ var isRequestType = (requestType, expected) => typeof requestType === "string" && requestType.toLowerCase() === expected.toLowerCase();
16618
+ var isProductScopedRequestType = (requestType) => PRODUCT_SCOPED_REQUEST_TYPES.some((type) => isRequestType(requestType, type));
16619
+
16620
+ // src/RedactoPrivacyCenter/lib/erasureConsent.ts
16621
+ var isErasureRequestType = (requestType) => isRequestType(requestType, "erasure" /* ERASURE */);
16622
+ var resolveErasurePurposeNames = (purposeNames, fallback) => {
16623
+ if (purposeNames.length === 0 || purposeNames.length > MAX_DISPLAYED_PURPOSE_NAMES_IN_ERASURE_MODAL) {
16624
+ return fallback;
16625
+ }
16626
+ return purposeNames.join(", ");
16627
+ };
16628
+ var erasureConsentPayload = (requestType, revokeConsentOnFulfilment) => {
16629
+ if (!isErasureRequestType(requestType)) {
16630
+ return {};
16631
+ }
16632
+ return { revoke_consent_on_fulfilment: revokeConsentOnFulfilment };
16633
+ };
16634
+
16574
16635
  // src/RedactoPrivacyCenter/lib/filePicker.ts
16575
16636
  import { Alert as Alert4 } from "react-native";
16576
16637
  async function pickFile() {
@@ -16807,12 +16868,6 @@ var buildGroupDataElements = (group) => {
16807
16868
  // src/RedactoPrivacyCenter/components/Form.tsx
16808
16869
  import { Fragment as Fragment9, jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
16809
16870
  var TOTAL_STEPS = 3;
16810
- var PRODUCT_SCOPED_REQUEST_TYPES = /* @__PURE__ */ new Set([
16811
- "ACCESS" /* ACCESS */,
16812
- "erasure" /* ERASURE */,
16813
- "CORRECTION" /* CORRECTION */
16814
- ]);
16815
- var isProductScopedRequestType = (rt) => PRODUCT_SCOPED_REQUEST_TYPES.has(rt);
16816
16871
  function Form({ onCreateRequest }) {
16817
16872
  const { t } = useTranslation15();
16818
16873
  const { accessToken, refreshToken, email, organizationId, workspaceId, updateTokens, onError } = useAuth();
@@ -16956,7 +17011,7 @@ function Form({ onCreateRequest }) {
16956
17011
  setSelectedProductKey(null);
16957
17012
  }
16958
17013
  setNominationData({ nominee_email: "", nominee_mobile: "" });
16959
- if (requestType === "grievance" /* GRIEVANCE */ && grievanceOptions.length > 0) {
17014
+ if (isRequestType(requestType, "grievance" /* GRIEVANCE */) && grievanceOptions.length > 0) {
16960
17015
  const resetGrievances = grievanceOptions.reduce(
16961
17016
  (acc, option) => {
16962
17017
  acc[option.value] = false;
@@ -16985,12 +17040,10 @@ function Form({ onCreateRequest }) {
16985
17040
  () => groupPurposes.filter((p) => selectedPurposes.has(p.uuid)).map((p) => p.name).filter(Boolean),
16986
17041
  [groupPurposes, selectedPurposes]
16987
17042
  );
16988
- const selectedErasurePurposeNames = useMemo14(() => {
16989
- if (selectedErasurePurposeList.length === 0 || selectedErasurePurposeList.length > MAX_DISPLAYED_PURPOSE_NAMES_IN_ERASURE_MODAL) {
16990
- return t("revokeConsentModalYourPurposes");
16991
- }
16992
- return selectedErasurePurposeList.join(", ");
16993
- }, [selectedErasurePurposeList, t]);
17043
+ const selectedErasurePurposeNames = useMemo14(
17044
+ () => resolveErasurePurposeNames(selectedErasurePurposeList, t("revokeConsentModalYourPurposes")),
17045
+ [selectedErasurePurposeList, t]
17046
+ );
16994
17047
  const togglePurpose = useCallback10(
16995
17048
  (purpose) => {
16996
17049
  const uuid = purpose.uuid;
@@ -17097,7 +17150,7 @@ function Form({ onCreateRequest }) {
17097
17150
  if (isProductScopedRequestType(requestType)) {
17098
17151
  if (allGroups.length === 0) return false;
17099
17152
  if (!selectedGroup) return false;
17100
- if (requestType === "CORRECTION" /* CORRECTION */) {
17153
+ if (isRequestType(requestType, "CORRECTION" /* CORRECTION */)) {
17101
17154
  if (selectedCorrectionFields.size === 0) return false;
17102
17155
  return Array.from(selectedCorrectionFields).every((fieldUuid) => {
17103
17156
  var _a3, _b;
@@ -17105,15 +17158,15 @@ function Form({ onCreateRequest }) {
17105
17158
  return Boolean(((_a3 = v == null ? void 0 : v.current) == null ? void 0 : _a3.trim()) && ((_b = v == null ? void 0 : v.updated) == null ? void 0 : _b.trim()));
17106
17159
  });
17107
17160
  }
17108
- if (requestType === "ACCESS" /* ACCESS */ || requestType === "erasure" /* ERASURE */) {
17161
+ if (isRequestType(requestType, "ACCESS" /* ACCESS */) || isErasureRequestType(requestType)) {
17109
17162
  return selectedPurposes.size > 0;
17110
17163
  }
17111
17164
  return false;
17112
17165
  }
17113
- if (requestType === "grievance" /* GRIEVANCE */) {
17166
+ if (isRequestType(requestType, "grievance" /* GRIEVANCE */)) {
17114
17167
  return Object.values(selectedGrievances).some(Boolean);
17115
17168
  }
17116
- if (requestType === "nomination" /* NOMINATION */) {
17169
+ if (isRequestType(requestType, "nomination" /* NOMINATION */)) {
17117
17170
  return Boolean(nominationData.nominee_email);
17118
17171
  }
17119
17172
  return false;
@@ -17127,7 +17180,10 @@ function Form({ onCreateRequest }) {
17127
17180
  selectedGrievances,
17128
17181
  nominationData
17129
17182
  ]);
17130
- const isFormValid = useMemo14(() => detailsValid && confirmChecked, [detailsValid, confirmChecked]);
17183
+ const isFormValid = useMemo14(
17184
+ () => Boolean(contact.trim()) && detailsValid && confirmChecked,
17185
+ [contact, detailsValid, confirmChecked]
17186
+ );
17131
17187
  const handleUpload = useCallback10(async () => {
17132
17188
  if (!organizationId || !workspaceId) return;
17133
17189
  const caseUuid = formDataResponse == null ? void 0 : formDataResponse.uuid;
@@ -17179,9 +17235,18 @@ function Form({ onCreateRequest }) {
17179
17235
  const removeUploadedDoc = useCallback10((uuid) => {
17180
17236
  setUploadedDocs((prev) => prev.filter((d) => d.uuid !== uuid));
17181
17237
  }, []);
17238
+ const alertSubmitFailed = useCallback10(
17239
+ (message) => {
17240
+ Alert5.alert(t("submit"), message || t("unableToSubmit") || "Unable to submit request. Please try again.");
17241
+ },
17242
+ [t]
17243
+ );
17182
17244
  const handleSubmit = useCallback10(async (revoke = false) => {
17183
- var _a3, _b, _c, _d;
17184
- if (!organizationId || !workspaceId || !contact || !requestType || !formDataResponse) return;
17245
+ var _a3;
17246
+ if (!organizationId || !workspaceId || !contact || !requestType || !formDataResponse) {
17247
+ alertSubmitFailed();
17248
+ return;
17249
+ }
17185
17250
  setRevokeConsentOnFulfilment(revoke);
17186
17251
  setIsSubmitting(true);
17187
17252
  try {
@@ -17208,27 +17273,27 @@ function Form({ onCreateRequest }) {
17208
17273
  purposes: updatePurposes,
17209
17274
  correction_data: correctionData,
17210
17275
  grievance_types: grievanceTypes,
17211
- ...requestType === "nomination" /* NOMINATION */ ? { nomination_data: nominationData } : {}
17276
+ ...isRequestType(requestType, "nomination" /* NOMINATION */) ? { nomination_data: nominationData } : {}
17212
17277
  },
17213
- time_period: requestType === "ACCESS" /* ACCESS */ ? parseInt(timePeriod || "30", 10) : 0,
17278
+ time_period: isRequestType(requestType, "ACCESS" /* ACCESS */) ? parseInt(timePeriod || "30", 10) : 0,
17214
17279
  requestor_note: additionalNote || "",
17215
- ...requestType === "erasure" /* ERASURE */ ? { revoke_consent_on_fulfilment: revoke } : {},
17280
+ ...erasureConsentPayload(requestType, revoke),
17216
17281
  ...anchorGroup ? { product_uuid: anchorGroup.product_uuid } : {},
17217
17282
  ...(anchorGroup == null ? void 0 : anchorGroup.nominator) ? { data_principal_uuid: anchorGroup.nominator.uuid } : {}
17218
17283
  };
17219
17284
  const response = await createCaseRequest(organizationId, workspaceId, request, false, false, authContext, consentClient);
17220
- const responseAny = response;
17221
- const newCaseId = (_c = (_b = responseAny == null ? void 0 : responseAny.case_id) != null ? _b : (_a3 = responseAny == null ? void 0 : responseAny.detail) == null ? void 0 : _a3.case_id) != null ? _c : null;
17285
+ const caseRef = extractCaseRef(response);
17286
+ const newCaseId = (caseRef == null ? void 0 : caseRef.case_id) || (caseRef == null ? void 0 : caseRef.uuid) || null;
17222
17287
  if (newCaseId) {
17223
17288
  setCaseId(newCaseId);
17224
- setSubmittedProductName((_d = anchorGroup == null ? void 0 : anchorGroup.product_name) != null ? _d : "");
17289
+ setSubmittedProductName((_a3 = anchorGroup == null ? void 0 : anchorGroup.product_name) != null ? _a3 : "");
17225
17290
  setIsSubmitted(true);
17226
17291
  } else {
17227
- Alert5.alert(t("submit"), t("unableToSubmit") || "Unable to submit request. Please try again.");
17292
+ alertSubmitFailed();
17228
17293
  }
17229
17294
  } catch (error) {
17230
17295
  console.warn("Error submitting request:", error);
17231
- Alert5.alert(t("submit"), (error == null ? void 0 : error.message) || t("unableToSubmit") || "Unable to submit request.");
17296
+ alertSubmitFailed(error == null ? void 0 : error.message);
17232
17297
  } finally {
17233
17298
  setIsSubmitting(false);
17234
17299
  }
@@ -17251,11 +17316,13 @@ function Form({ onCreateRequest }) {
17251
17316
  consentClient,
17252
17317
  groupDataElements,
17253
17318
  uploadedDocs,
17254
- t
17319
+ alertSubmitFailed
17255
17320
  ]);
17256
17321
  const handleSubmitClick = useCallback10(() => {
17257
- if (!isFormValid) return;
17258
- if (requestType === "erasure" /* ERASURE */) {
17322
+ if (!isFormValid) {
17323
+ return;
17324
+ }
17325
+ if (isErasureRequestType(requestType)) {
17259
17326
  setIsErasureConsentModalOpen(true);
17260
17327
  return;
17261
17328
  }
@@ -17344,7 +17411,7 @@ function Form({ onCreateRequest }) {
17344
17411
  if (groupPurposes.length === 0) {
17345
17412
  return /* @__PURE__ */ jsx43(Text25, { style: [styles33.placeholder, { color: colors.textSecondary }], children: t("noPurposesAvailable") || "No purposes available" });
17346
17413
  }
17347
- const isErasure = requestType === "erasure" /* ERASURE */;
17414
+ const isErasure = isErasureRequestType(requestType);
17348
17415
  const allPurposesSelected = groupPurposes.every(
17349
17416
  (p) => (p.data_elements || []).every((de) => {
17350
17417
  var _a3;
@@ -17464,18 +17531,18 @@ function Form({ onCreateRequest }) {
17464
17531
  ] }),
17465
17532
  selectedGroup ? /* @__PURE__ */ jsxs27(View32, { style: styles33.section, children: [
17466
17533
  /* @__PURE__ */ jsx43(Text25, { style: [styles33.sectionLabel, { color: colors.textSecondary }], children: t("requestDetails") }),
17467
- /* @__PURE__ */ jsx43(View32, { style: [styles33.sectionCard, elevation.card, { backgroundColor: colors.surface }], children: requestType === "CORRECTION" /* CORRECTION */ ? renderCorrectionFields() : renderPurposeFields() })
17534
+ /* @__PURE__ */ jsx43(View32, { style: [styles33.sectionCard, elevation.card, { backgroundColor: colors.surface }], children: isRequestType(requestType, "CORRECTION" /* CORRECTION */) ? renderCorrectionFields() : renderPurposeFields() })
17468
17535
  ] }) : /* @__PURE__ */ jsxs27(View32, { style: [styles33.hintCard, { backgroundColor: colors.surfaceAlt }], children: [
17469
17536
  /* @__PURE__ */ jsx43(Icon, { name: "circle-alert", size: 18, color: colors.textSecondary }),
17470
17537
  /* @__PURE__ */ jsx43(Text25, { style: [styles33.hintText, { color: colors.textSecondary }], children: t("selectProductFirst", { defaultValue: "Select a product to continue" }) })
17471
17538
  ] }),
17472
- requestType === "ACCESS" /* ACCESS */ && selectedGroup ? /* @__PURE__ */ jsxs27(View32, { style: styles33.section, children: [
17539
+ isRequestType(requestType, "ACCESS" /* ACCESS */) && selectedGroup ? /* @__PURE__ */ jsxs27(View32, { style: styles33.section, children: [
17473
17540
  /* @__PURE__ */ jsx43(Text25, { style: [styles33.sectionLabel, { color: colors.textSecondary }], children: t("timePeriod") }),
17474
17541
  /* @__PURE__ */ jsx43(PCSelect, { value: timePeriod, options: timePeriodOptions, onChange: setTimePeriod, placeholder: t("timePeriod") })
17475
17542
  ] }) : null
17476
17543
  ] });
17477
17544
  }
17478
- if (requestType === "grievance" /* GRIEVANCE */) {
17545
+ if (isRequestType(requestType, "grievance" /* GRIEVANCE */)) {
17479
17546
  if (grievanceOptions.length === 0) {
17480
17547
  return /* @__PURE__ */ jsx43(View32, { style: styles33.stepBody, children: /* @__PURE__ */ jsx43(Text25, { style: [styles33.placeholder, { color: colors.textSecondary }], children: t("noPurposesAvailable") || "No grievance types available" }) });
17481
17548
  }
@@ -17491,7 +17558,7 @@ function Form({ onCreateRequest }) {
17491
17558
  ] })
17492
17559
  ] }) });
17493
17560
  }
17494
- if (requestType === "nomination" /* NOMINATION */) {
17561
+ if (isRequestType(requestType, "nomination" /* NOMINATION */)) {
17495
17562
  return /* @__PURE__ */ jsx43(View32, { style: styles33.stepBody, children: /* @__PURE__ */ jsxs27(View32, { style: styles33.section, children: [
17496
17563
  /* @__PURE__ */ jsx43(Text25, { style: [styles33.sectionLabel, { color: colors.textSecondary }], children: t("enterNomineeDetails") }),
17497
17564
  /* @__PURE__ */ jsxs27(View32, { style: [styles33.sectionCard, elevation.card, { backgroundColor: colors.surface }], children: [
@@ -18473,6 +18540,22 @@ import { useMemo as useMemo18, useState as useState21 } from "react";
18473
18540
  import { StyleSheet as StyleSheet39, Text as Text29, TouchableOpacity as TouchableOpacity14, View as View37 } from "react-native";
18474
18541
  import { useTranslation as useTranslation18 } from "react-i18next";
18475
18542
 
18543
+ // src/RedactoPrivacyCenter/context/ProfileSwitchContext.tsx
18544
+ import { createContext as createContext7, useContext as useContext7 } from "react";
18545
+ import { jsx as jsx46 } from "react/jsx-runtime";
18546
+ var ProfileSwitchContext = createContext7({
18547
+ switchProfile: null
18548
+ });
18549
+ function ProfileSwitchProvider({
18550
+ switchProfile,
18551
+ children
18552
+ }) {
18553
+ return /* @__PURE__ */ jsx46(ProfileSwitchContext.Provider, { value: { switchProfile }, children });
18554
+ }
18555
+ function useProfileSwitch() {
18556
+ return useContext7(ProfileSwitchContext);
18557
+ }
18558
+
18476
18559
  // src/RedactoPrivacyCenter/lib/navigation.ts
18477
18560
  function navigateBack(onBack) {
18478
18561
  var _a3, _b;
@@ -18496,7 +18579,7 @@ function navigateBack(onBack) {
18496
18579
 
18497
18580
  // src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx
18498
18581
  import { StyleSheet as StyleSheet37, Text as Text27, TouchableOpacity as TouchableOpacity12, View as View35 } from "react-native";
18499
- import { jsx as jsx46, jsxs as jsxs29 } from "react/jsx-runtime";
18582
+ import { jsx as jsx47, jsxs as jsxs29 } from "react/jsx-runtime";
18500
18583
  var NotificationBell = ({ count, onPress }) => {
18501
18584
  const { colors } = useTheme();
18502
18585
  const showBadge = count > 0;
@@ -18511,8 +18594,8 @@ var NotificationBell = ({ count, onPress }) => {
18511
18594
  accessibilityRole: "button",
18512
18595
  accessibilityLabel: showBadge ? `Notifications, ${count} unread` : "Notifications",
18513
18596
  children: [
18514
- /* @__PURE__ */ jsx46(Icon, { name: "bell", size: 22, color: colors.text }),
18515
- showBadge ? /* @__PURE__ */ jsx46(View35, { style: [styles36.badge, { backgroundColor: colors.error, borderColor: colors.canvasSubtle }], children: /* @__PURE__ */ jsx46(Text27, { style: styles36.badgeText, numberOfLines: 1, children: badgeLabel }) }) : null
18597
+ /* @__PURE__ */ jsx47(Icon, { name: "bell", size: 22, color: colors.text }),
18598
+ showBadge ? /* @__PURE__ */ jsx47(View35, { style: [styles36.badge, { backgroundColor: colors.error, borderColor: colors.canvasSubtle }], children: /* @__PURE__ */ jsx47(Text27, { style: styles36.badgeText, numberOfLines: 1, children: badgeLabel }) }) : null
18516
18599
  ]
18517
18600
  }
18518
18601
  );
@@ -18578,7 +18661,7 @@ function useUnreadCount() {
18578
18661
  import { useMemo as useMemo17, useState as useState20 } from "react";
18579
18662
  import { StyleSheet as StyleSheet38, Text as Text28, TouchableOpacity as TouchableOpacity13, View as View36 } from "react-native";
18580
18663
  import { useTranslation as useTranslation17 } from "react-i18next";
18581
- import { jsx as jsx47, jsxs as jsxs30 } from "react/jsx-runtime";
18664
+ import { jsx as jsx48, jsxs as jsxs30 } from "react/jsx-runtime";
18582
18665
  var NavDrawer = ({ visible, onClose, onBack }) => {
18583
18666
  const { t } = useTranslation17();
18584
18667
  const { colors, branding } = useTheme();
@@ -18600,15 +18683,15 @@ var NavDrawer = ({ visible, onClose, onBack }) => {
18600
18683
  i18n_default.changeLanguage(value);
18601
18684
  };
18602
18685
  const brandName = (branding == null ? void 0 : branding.brand_name) || organizationName || t("privacyCenter") || "Privacy Center";
18603
- return /* @__PURE__ */ jsx47(BottomSheet, { visible, onClose, title: t("menu") || "Menu", children: /* @__PURE__ */ jsxs30(View36, { style: styles37.body, children: [
18686
+ return /* @__PURE__ */ jsx48(BottomSheet, { visible, onClose, title: t("menu") || "Menu", children: /* @__PURE__ */ jsxs30(View36, { style: styles37.body, children: [
18604
18687
  /* @__PURE__ */ jsxs30(View36, { style: [styles37.identity, { backgroundColor: colors.surfaceAlt }], children: [
18605
- /* @__PURE__ */ jsx47(PCAvatar, { name: brandName, imageUrl: branding == null ? void 0 : branding.icon_url, size: 40 }),
18688
+ /* @__PURE__ */ jsx48(PCAvatar, { name: brandName, imageUrl: branding == null ? void 0 : branding.icon_url, size: 40 }),
18606
18689
  /* @__PURE__ */ jsxs30(View36, { style: styles37.identityText, children: [
18607
- /* @__PURE__ */ jsx47(Text28, { style: [styles37.brandName, { color: colors.text }], numberOfLines: 1, children: brandName }),
18608
- email ? /* @__PURE__ */ jsx47(Text28, { style: [styles37.email, { color: colors.textSecondary }], numberOfLines: 1, children: email }) : null
18690
+ /* @__PURE__ */ jsx48(Text28, { style: [styles37.brandName, { color: colors.text }], numberOfLines: 1, children: brandName }),
18691
+ email ? /* @__PURE__ */ jsx48(Text28, { style: [styles37.email, { color: colors.textSecondary }], numberOfLines: 1, children: email }) : null
18609
18692
  ] })
18610
18693
  ] }),
18611
- /* @__PURE__ */ jsx47(View36, { style: [styles37.menuGroup, { borderColor: colors.border, backgroundColor: colors.surface }], children: items.map((item, index) => {
18694
+ /* @__PURE__ */ jsx48(View36, { style: [styles37.menuGroup, { borderColor: colors.border, backgroundColor: colors.surface }], children: items.map((item, index) => {
18612
18695
  const isActive = currentPage === item.page;
18613
18696
  return /* @__PURE__ */ jsxs30(View36, { children: [
18614
18697
  /* @__PURE__ */ jsxs30(
@@ -18624,8 +18707,8 @@ var NavDrawer = ({ visible, onClose, onBack }) => {
18624
18707
  accessibilityState: { selected: isActive },
18625
18708
  accessibilityLabel: item.label,
18626
18709
  children: [
18627
- /* @__PURE__ */ jsx47(IconTile, { name: item.icon, tone: item.tone, size: "sm" }),
18628
- /* @__PURE__ */ jsx47(
18710
+ /* @__PURE__ */ jsx48(IconTile, { name: item.icon, tone: item.tone, size: "sm" }),
18711
+ /* @__PURE__ */ jsx48(
18629
18712
  Text28,
18630
18713
  {
18631
18714
  style: [styles37.menuLabel, { color: isActive ? colors.primary : colors.text }],
@@ -18633,14 +18716,14 @@ var NavDrawer = ({ visible, onClose, onBack }) => {
18633
18716
  children: item.label
18634
18717
  }
18635
18718
  ),
18636
- /* @__PURE__ */ jsx47(Icon, { name: "chevron-right", size: 18, color: colors.textSecondary })
18719
+ /* @__PURE__ */ jsx48(Icon, { name: "chevron-right", size: 18, color: colors.textSecondary })
18637
18720
  ]
18638
18721
  }
18639
18722
  ),
18640
- index < items.length - 1 ? /* @__PURE__ */ jsx47(View36, { style: [styles37.separator, { backgroundColor: colors.hairline }] }) : null
18723
+ index < items.length - 1 ? /* @__PURE__ */ jsx48(View36, { style: [styles37.separator, { backgroundColor: colors.hairline }] }) : null
18641
18724
  ] }, item.page);
18642
18725
  }) }),
18643
- /* @__PURE__ */ jsx47(
18726
+ /* @__PURE__ */ jsx48(
18644
18727
  PCSelect,
18645
18728
  {
18646
18729
  label: t("language"),
@@ -18661,7 +18744,7 @@ var NavDrawer = ({ visible, onClose, onBack }) => {
18661
18744
  activeOpacity: 0.7,
18662
18745
  accessibilityRole: "button",
18663
18746
  children: [
18664
- /* @__PURE__ */ jsx47(
18747
+ /* @__PURE__ */ jsx48(
18665
18748
  Icon,
18666
18749
  {
18667
18750
  name: onBack === "signout" ? "log-out" : "arrow-left",
@@ -18669,11 +18752,11 @@ var NavDrawer = ({ visible, onClose, onBack }) => {
18669
18752
  color: onBack === "signout" ? colors.error : colors.text
18670
18753
  }
18671
18754
  ),
18672
- /* @__PURE__ */ jsx47(Text28, { style: [styles37.actionLabel, { color: onBack === "signout" ? colors.error : colors.text }], children: onBack === "signout" ? t("signOut") || "Sign Out" : t("back") || "Back" })
18755
+ /* @__PURE__ */ jsx48(Text28, { style: [styles37.actionLabel, { color: onBack === "signout" ? colors.error : colors.text }], children: onBack === "signout" ? t("signOut") || "Sign Out" : t("back") || "Back" })
18673
18756
  ]
18674
18757
  }
18675
18758
  ) : null,
18676
- /* @__PURE__ */ jsx47(Footer, { inline: true })
18759
+ /* @__PURE__ */ jsx48(Footer, { inline: true })
18677
18760
  ] }) });
18678
18761
  };
18679
18762
  var styles37 = StyleSheet38.create({
@@ -18735,7 +18818,7 @@ var styles37 = StyleSheet38.create({
18735
18818
  });
18736
18819
 
18737
18820
  // src/RedactoPrivacyCenter/components/SlimTopBar.tsx
18738
- import { Fragment as Fragment11, jsx as jsx48, jsxs as jsxs31 } from "react/jsx-runtime";
18821
+ import { Fragment as Fragment11, jsx as jsx49, jsxs as jsxs31 } from "react/jsx-runtime";
18739
18822
  var SlimTopBar = ({ mode = "primary", onBack }) => {
18740
18823
  const { t } = useTranslation18();
18741
18824
  const { currentPage, navigateTo } = useInternalNavigation();
@@ -18744,6 +18827,7 @@ var SlimTopBar = ({ mode = "primary", onBack }) => {
18744
18827
  const unreadCount = useUnreadCount();
18745
18828
  const insets = useSafeInsets();
18746
18829
  const [menuOpen, setMenuOpen] = useState21(false);
18830
+ const { switchProfile } = useProfileSwitch();
18747
18831
  const pageChrome = PAGE_CHROME_BY_PAGE[currentPage];
18748
18832
  const isDetail = mode === "detail";
18749
18833
  const brandLabel = useMemo18(
@@ -18762,7 +18846,7 @@ var SlimTopBar = ({ mode = "primary", onBack }) => {
18762
18846
  navigateBack(onBack);
18763
18847
  };
18764
18848
  return /* @__PURE__ */ jsxs31(Fragment11, { children: [
18765
- /* @__PURE__ */ jsx48(
18849
+ /* @__PURE__ */ jsx49(
18766
18850
  View37,
18767
18851
  {
18768
18852
  style: [
@@ -18770,7 +18854,7 @@ var SlimTopBar = ({ mode = "primary", onBack }) => {
18770
18854
  { backgroundColor: colors.canvasSubtle, borderBottomColor: colors.hairline, paddingTop: insets.top + spacing.xs }
18771
18855
  ],
18772
18856
  children: isDetail ? /* @__PURE__ */ jsxs31(View37, { style: styles38.detailRow, children: [
18773
- /* @__PURE__ */ jsx48(
18857
+ /* @__PURE__ */ jsx49(
18774
18858
  TouchableOpacity14,
18775
18859
  {
18776
18860
  style: styles38.iconButton,
@@ -18779,27 +18863,39 @@ var SlimTopBar = ({ mode = "primary", onBack }) => {
18779
18863
  hitSlop: DEFAULT_HIT_SLOP,
18780
18864
  accessibilityRole: "button",
18781
18865
  accessibilityLabel: t("back") || "Back",
18782
- children: /* @__PURE__ */ jsx48(Icon, { name: "arrow-left", size: 22, color: colors.text })
18866
+ children: /* @__PURE__ */ jsx49(Icon, { name: "arrow-left", size: 22, color: colors.text })
18783
18867
  }
18784
18868
  ),
18785
- /* @__PURE__ */ jsx48(Text29, { style: [styles38.detailTitle, { color: colors.text }], numberOfLines: 1, children: t((pageChrome == null ? void 0 : pageChrome.titleKey) || "privacyCenter") }),
18786
- /* @__PURE__ */ jsx48(View37, { style: styles38.placeholder })
18869
+ /* @__PURE__ */ jsx49(Text29, { style: [styles38.detailTitle, { color: colors.text }], numberOfLines: 1, children: t((pageChrome == null ? void 0 : pageChrome.titleKey) || "privacyCenter") }),
18870
+ /* @__PURE__ */ jsx49(View37, { style: styles38.placeholder })
18787
18871
  ] }) : /* @__PURE__ */ jsxs31(View37, { style: styles38.row, children: [
18788
- /* @__PURE__ */ jsx48(View37, { style: styles38.brandWrap, children: (branding == null ? void 0 : branding.logo_url) ? /* @__PURE__ */ jsx48(
18872
+ /* @__PURE__ */ jsx49(View37, { style: styles38.brandWrap, children: (branding == null ? void 0 : branding.logo_url) ? /* @__PURE__ */ jsx49(
18789
18873
  BrandImage,
18790
18874
  {
18791
18875
  uri: branding.logo_url,
18792
18876
  height: 24,
18793
18877
  maxWidth: 124,
18794
- fallback: /* @__PURE__ */ jsx48(PCAvatar, { name: brandLabel, imageUrl: branding == null ? void 0 : branding.icon_url, size: 24 })
18878
+ fallback: /* @__PURE__ */ jsx49(PCAvatar, { name: brandLabel, imageUrl: branding == null ? void 0 : branding.icon_url, size: 24 })
18795
18879
  }
18796
18880
  ) : /* @__PURE__ */ jsxs31(View37, { style: styles38.brandLockup, children: [
18797
- /* @__PURE__ */ jsx48(PCAvatar, { name: brandLabel, imageUrl: branding == null ? void 0 : branding.icon_url, size: 24 }),
18798
- /* @__PURE__ */ jsx48(Text29, { style: [styles38.brandText, { color: colors.text }], numberOfLines: 1, children: brandLabel })
18881
+ /* @__PURE__ */ jsx49(PCAvatar, { name: brandLabel, imageUrl: branding == null ? void 0 : branding.icon_url, size: 24 }),
18882
+ /* @__PURE__ */ jsx49(Text29, { style: [styles38.brandText, { color: colors.text }], numberOfLines: 1, children: brandLabel })
18799
18883
  ] }) }),
18800
18884
  /* @__PURE__ */ jsxs31(View37, { style: styles38.actions, children: [
18801
- /* @__PURE__ */ jsx48(NotificationBell, { count: unreadCount, onPress: () => navigateTo("notifications") }),
18802
- /* @__PURE__ */ jsx48(
18885
+ switchProfile ? /* @__PURE__ */ jsx49(
18886
+ TouchableOpacity14,
18887
+ {
18888
+ style: styles38.iconButton,
18889
+ onPress: switchProfile,
18890
+ activeOpacity: 0.7,
18891
+ hitSlop: DEFAULT_HIT_SLOP,
18892
+ accessibilityRole: "button",
18893
+ accessibilityLabel: t("switchProfile"),
18894
+ children: /* @__PURE__ */ jsx49(Icon, { name: "users", size: 22, color: colors.text })
18895
+ }
18896
+ ) : null,
18897
+ /* @__PURE__ */ jsx49(NotificationBell, { count: unreadCount, onPress: () => navigateTo("notifications") }),
18898
+ /* @__PURE__ */ jsx49(
18803
18899
  TouchableOpacity14,
18804
18900
  {
18805
18901
  style: styles38.iconButton,
@@ -18808,14 +18904,14 @@ var SlimTopBar = ({ mode = "primary", onBack }) => {
18808
18904
  hitSlop: DEFAULT_HIT_SLOP,
18809
18905
  accessibilityRole: "button",
18810
18906
  accessibilityLabel: t("menu") || "Menu",
18811
- children: /* @__PURE__ */ jsx48(Icon, { name: "menu", size: 22, color: colors.text })
18907
+ children: /* @__PURE__ */ jsx49(Icon, { name: "menu", size: 22, color: colors.text })
18812
18908
  }
18813
18909
  )
18814
18910
  ] })
18815
18911
  ] })
18816
18912
  }
18817
18913
  ),
18818
- /* @__PURE__ */ jsx48(NavDrawer, { visible: menuOpen, onClose: () => setMenuOpen(false), onBack })
18914
+ /* @__PURE__ */ jsx49(NavDrawer, { visible: menuOpen, onClose: () => setMenuOpen(false), onBack })
18819
18915
  ] });
18820
18916
  };
18821
18917
  var styles38 = StyleSheet39.create({
@@ -18876,18 +18972,18 @@ var styles38 = StyleSheet39.create({
18876
18972
 
18877
18973
  // src/RedactoPrivacyCenter/components/PrivacyCenterEmptyState.tsx
18878
18974
  import { useTranslation as useTranslation19 } from "react-i18next";
18879
- import { jsx as jsx49 } from "react/jsx-runtime";
18975
+ import { jsx as jsx50 } from "react/jsx-runtime";
18880
18976
  function PrivacyCenterEmptyState() {
18881
18977
  const { t } = useTranslation19();
18882
- return /* @__PURE__ */ jsx49(PCEmpty, { iconName: "shield", title: t("noDataTitle"), description: t("noDataDescription") });
18978
+ return /* @__PURE__ */ jsx50(PCEmpty, { iconName: "shield", title: t("noDataTitle"), description: t("noDataDescription") });
18883
18979
  }
18884
18980
 
18885
18981
  // src/RedactoPrivacyCenter/components/PrivacyCenterSessionExpired.tsx
18886
18982
  import { useTranslation as useTranslation20 } from "react-i18next";
18887
- import { jsx as jsx50 } from "react/jsx-runtime";
18983
+ import { jsx as jsx51 } from "react/jsx-runtime";
18888
18984
  function PrivacyCenterSessionExpired() {
18889
18985
  const { t } = useTranslation20();
18890
- return /* @__PURE__ */ jsx50(
18986
+ return /* @__PURE__ */ jsx51(
18891
18987
  PCEmpty,
18892
18988
  {
18893
18989
  iconName: "shield",
@@ -18901,22 +18997,6 @@ function PrivacyCenterSessionExpired() {
18901
18997
  import { useCallback as useCallback11, useEffect as useEffect16, useState as useState22 } from "react";
18902
18998
  import { StyleSheet as StyleSheet41, View as View39 } from "react-native";
18903
18999
 
18904
- // src/RedactoPrivacyCenter/context/ProfileSwitchContext.tsx
18905
- import { createContext as createContext7, useContext as useContext7 } from "react";
18906
- import { jsx as jsx51 } from "react/jsx-runtime";
18907
- var ProfileSwitchContext = createContext7({
18908
- switchProfile: null
18909
- });
18910
- function ProfileSwitchProvider({
18911
- switchProfile,
18912
- children
18913
- }) {
18914
- return /* @__PURE__ */ jsx51(ProfileSwitchContext.Provider, { value: { switchProfile }, children });
18915
- }
18916
- function useProfileSwitch() {
18917
- return useContext7(ProfileSwitchContext);
18918
- }
18919
-
18920
19000
  // src/RedactoPrivacyCenter/components/SelectUser.tsx
18921
19001
  import { ScrollView as ScrollView8, StyleSheet as StyleSheet40, Text as Text30, TouchableOpacity as TouchableOpacity15, View as View38 } from "react-native";
18922
19002
  import { useTranslation as useTranslation21 } from "react-i18next";
@@ -21692,6 +21772,7 @@ function Navbar({ mode = "primary", onBack }) {
21692
21772
  const [menuOpen, setMenuOpen] = useState26(false);
21693
21773
  const unreadCount = useUnreadCount();
21694
21774
  const { status: pageStatus } = usePageStatus();
21775
+ const { switchProfile } = useProfileSwitch();
21695
21776
  const pageChrome = PAGE_CHROME_BY_PAGE[currentPage] || PAGE_CHROME_BY_PAGE["consent-manager"];
21696
21777
  const isDetail = mode === "detail";
21697
21778
  useEffect22(() => {
@@ -21794,6 +21875,36 @@ function Navbar({ mode = "primary", onBack }) {
21794
21875
  ) : null,
21795
21876
  /* @__PURE__ */ jsx71(View55, { style: styles57.brandWrap, children: (branding == null ? void 0 : branding.logo_url) ? /* @__PURE__ */ jsx71(BrandImage, { uri: branding.logo_url, height: 34, maxWidth: 170, fallback: brandLockup }) : brandLockup }),
21796
21877
  /* @__PURE__ */ jsxs48(View55, { style: styles57.actions, children: [
21878
+ switchProfile ? /* @__PURE__ */ jsx71(
21879
+ TouchableOpacity25,
21880
+ {
21881
+ style: styles57.iconButton,
21882
+ onPress: switchProfile,
21883
+ activeOpacity: 0.7,
21884
+ hitSlop: DEFAULT_HIT_SLOP,
21885
+ accessibilityRole: "button",
21886
+ accessibilityLabel: t("switchProfile"),
21887
+ children: /* @__PURE__ */ jsx71(
21888
+ View55,
21889
+ {
21890
+ style: [
21891
+ styles57.iconCircle,
21892
+ { backgroundColor: colors.surface, borderColor: colors.border }
21893
+ ],
21894
+ children: /* @__PURE__ */ jsx71(Svg4, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx71(
21895
+ Path4,
21896
+ {
21897
+ d: "M16 3h5v5 M21 3l-7 7 M8 21H3v-5 M3 21l7-7",
21898
+ stroke: colors.text,
21899
+ strokeWidth: 2,
21900
+ strokeLinecap: "round",
21901
+ strokeLinejoin: "round"
21902
+ }
21903
+ ) })
21904
+ }
21905
+ )
21906
+ }
21907
+ ) : null,
21797
21908
  /* @__PURE__ */ jsx71(NotificationBell, { count: unreadCount, onPress: () => navigateTo("notifications") }),
21798
21909
  /* @__PURE__ */ jsx71(PCSelect, { value: locale, options: languageOptions, onChange: handleLanguageChange, placeholder: t("language"), compact: true }),
21799
21910
  /* @__PURE__ */ jsx71(
@@ -21870,6 +21981,21 @@ var styles57 = StyleSheet58.create({
21870
21981
  alignItems: "center",
21871
21982
  justifyContent: "center"
21872
21983
  },
21984
+ iconButton: {
21985
+ width: MIN_TAP_TARGET,
21986
+ height: MIN_TAP_TARGET,
21987
+ alignItems: "center",
21988
+ justifyContent: "center"
21989
+ },
21990
+ // Matches the Flutter navbar's 34pt bordered circle.
21991
+ iconCircle: {
21992
+ width: 34,
21993
+ height: 34,
21994
+ borderRadius: 17,
21995
+ borderWidth: 1,
21996
+ alignItems: "center",
21997
+ justifyContent: "center"
21998
+ },
21873
21999
  pageHeader: {
21874
22000
  marginTop: spacing.sm
21875
22001
  },