@redacto.io/consent-sdk-react 9.5.0-beta.0 → 10.0.0-beta.1

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.
Files changed (66) hide show
  1. package/.turbo/turbo-build.log +22 -25
  2. package/CHANGELOG.md +16 -2
  3. package/dist/index.d.mts +13 -4
  4. package/dist/index.d.ts +13 -4
  5. package/dist/index.js +116 -11
  6. package/dist/index.mjs +116 -11
  7. package/dist/privacy-center.d.mts +76 -19
  8. package/dist/privacy-center.d.ts +76 -19
  9. package/dist/privacy-center.js +2022 -1378
  10. package/dist/privacy-center.mjs +1946 -1297
  11. package/package.json +1 -1
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +5 -0
  13. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +29 -3
  14. package/src/RedactoNoticeConsent/api/index.ts +130 -1
  15. package/src/RedactoNoticeConsent/api/types.ts +33 -0
  16. package/src/RedactoNoticeConsent/types.ts +1 -0
  17. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +3 -0
  18. package/src/RedactoNoticeConsentInline/api/index.ts +4 -1
  19. package/src/RedactoNoticeConsentInline/api/types.ts +1 -0
  20. package/src/RedactoNoticeConsentInline/types.ts +1 -0
  21. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +8 -5
  22. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +3 -1
  23. package/src/RedactoPrivacyCenter/api/actions.ts +15 -2
  24. package/src/RedactoPrivacyCenter/api/client.ts +146 -20
  25. package/src/RedactoPrivacyCenter/components/AppShell/UserMenu.tsx +64 -35
  26. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +12 -1
  27. package/src/RedactoPrivacyCenter/components/Form.tsx +63 -7
  28. package/src/RedactoPrivacyCenter/components/SelectUser.tsx +83 -0
  29. package/src/RedactoPrivacyCenter/components/SelectUserGate.tsx +193 -0
  30. package/src/RedactoPrivacyCenter/components/constant.ts +11 -0
  31. package/src/RedactoPrivacyCenter/components/groupHelpers.ts +64 -1
  32. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +8 -2
  33. package/src/RedactoPrivacyCenter/context/ProfileSwitchContext.tsx +15 -0
  34. package/src/RedactoPrivacyCenter/context/types.ts +1 -0
  35. package/src/RedactoPrivacyCenter/lib/api.ts +8 -0
  36. package/src/RedactoPrivacyCenter/lib/constants.ts +9 -0
  37. package/src/RedactoPrivacyCenter/lib/types.ts +63 -1
  38. package/src/RedactoPrivacyCenter/locales/as/translation.json +7 -1
  39. package/src/RedactoPrivacyCenter/locales/bho/translation.json +7 -1
  40. package/src/RedactoPrivacyCenter/locales/bn/translation.json +7 -1
  41. package/src/RedactoPrivacyCenter/locales/brx/translation.json +7 -1
  42. package/src/RedactoPrivacyCenter/locales/doi/translation.json +7 -1
  43. package/src/RedactoPrivacyCenter/locales/en/translation.json +7 -1
  44. package/src/RedactoPrivacyCenter/locales/gom/translation.json +7 -1
  45. package/src/RedactoPrivacyCenter/locales/gu/translation.json +7 -1
  46. package/src/RedactoPrivacyCenter/locales/hi/translation.json +7 -1
  47. package/src/RedactoPrivacyCenter/locales/kn/translation.json +7 -1
  48. package/src/RedactoPrivacyCenter/locales/ks/translation.json +7 -1
  49. package/src/RedactoPrivacyCenter/locales/mai/translation.json +7 -1
  50. package/src/RedactoPrivacyCenter/locales/ml/translation.json +7 -1
  51. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +7 -1
  52. package/src/RedactoPrivacyCenter/locales/mr/translation.json +7 -1
  53. package/src/RedactoPrivacyCenter/locales/ne/translation.json +7 -1
  54. package/src/RedactoPrivacyCenter/locales/or/translation.json +7 -1
  55. package/src/RedactoPrivacyCenter/locales/pa/translation.json +7 -1
  56. package/src/RedactoPrivacyCenter/locales/sa/translation.json +7 -1
  57. package/src/RedactoPrivacyCenter/locales/sat/translation.json +7 -1
  58. package/src/RedactoPrivacyCenter/locales/sd/translation.json +7 -1
  59. package/src/RedactoPrivacyCenter/locales/ta/translation.json +7 -1
  60. package/src/RedactoPrivacyCenter/locales/te/translation.json +7 -1
  61. package/src/RedactoPrivacyCenter/locales/ur/translation.json +7 -1
  62. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +105 -0
  63. package/tests/Form.test.tsx +58 -7
  64. package/tests/consentClient.test.ts +171 -0
  65. package/tests/manageConsentServer.test.ts +38 -0
  66. package/tests/mocks.ts +1 -0
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "പ്രൊഫൈൽ മാറ്റുക",
335
+ "chooseYourProfile": "നിങ്ങളുടെ പ്രൊഫൈൽ തിരഞ്ഞെടുക്കുക",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ഒന്നിലധികം പ്രൊഫൈലുകളുമായി ബന്ധിപ്പിച്ചിരിക്കുന്നു. നിങ്ങൾ കാണാൻ ആഗ്രഹിക്കുന്നത് തിരഞ്ഞെടുക്കുക.",
337
+ "contactLinkedToMultipleProfiles": "ഈ കോൺടാക്റ്റ് ഒന്നിലധികം പ്രൊഫൈലുകളുമായി ബന്ധിപ്പിച്ചിരിക്കുന്നു. നിങ്ങൾ കാണാൻ ആഗ്രഹിക്കുന്നത് തിരഞ്ഞെടുക്കുക.",
338
+ "signingInEllipsis": "സൈൻ ഇൻ ചെയ്യുന്നു…",
339
+ "couldNotSwitchProfile": "ആ പ്രൊഫൈലിലേക്ക് മാറാൻ കഴിഞ്ഞില്ല. വീണ്ടും ശ്രമിക്കുക."
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "প্রোফাইল হোংদোক-উ",
335
+ "chooseYourProfile": "নহাক্কী প্রোফাইল খনবিয়ু",
336
+ "emailLinkedToMultipleProfiles": "{{email}} অসি প্রোফাইল অমদগী হেন্না লোইননবা লৈ। নহাক্না য়েংনিংবা অদু খনবিয়ু।",
337
+ "contactLinkedToMultipleProfiles": "মসি কন্টেক্ট অসি প্রোফাইল অমদগী হেন্না লোইননবা লৈ। নহাক্না য়েংনিংবা অদু খনবিয়ু।",
338
+ "signingInEllipsis": "সাইন ইন তৌরি…",
339
+ "couldNotSwitchProfile": "প্রোফাইল অদুদা হোংদোকপা ঙমদ্রে। চানবীদুনা অমুক হন্না হোৎনবিয়ু।"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "प्रोफाइल बदला",
335
+ "chooseYourProfile": "तुमची प्रोफाइल निवडा",
336
+ "emailLinkedToMultipleProfiles": "{{email}} एकापेक्षा जास्त प्रोफाइलशी जोडलेला आहे. तुम्हाला जी पाहायची आहे ती निवडा.",
337
+ "contactLinkedToMultipleProfiles": "हा संपर्क एकापेक्षा जास्त प्रोफाइलशी जोडलेला आहे. तुम्हाला जी पाहायची आहे ती निवडा.",
338
+ "signingInEllipsis": "साइन इन करत आहे…",
339
+ "couldNotSwitchProfile": "त्या प्रोफाइलवर स्विच करता आले नाही. कृपया पुन्हा प्रयत्न करा."
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "प्रोफाइल बदल्नुहोस्",
335
+ "chooseYourProfile": "आफ्नो प्रोफाइल छान्नुहोस्",
336
+ "emailLinkedToMultipleProfiles": "{{email}} एकभन्दा बढी प्रोफाइलसँग जोडिएको छ। तपाईंले हेर्न चाहनुभएको छान्नुहोस्।",
337
+ "contactLinkedToMultipleProfiles": "यो सम्पर्क एकभन्दा बढी प्रोफाइलसँग जोडिएको छ। तपाईंले हेर्न चाहनुभएको छान्नुहोस्।",
338
+ "signingInEllipsis": "साइन इन हुँदैछ…",
339
+ "couldNotSwitchProfile": "त्यो प्रोफाइलमा स्विच गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "ପ୍ରୋଫାଇଲ୍ ବଦଳାନ୍ତୁ",
335
+ "chooseYourProfile": "ଆପଣଙ୍କ ପ୍ରୋଫାଇଲ୍ ବାଛନ୍ତୁ",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ଏକାଧିକ ପ୍ରୋଫାଇଲ୍ ସହିତ ଯୋଡ଼ାଯାଇଛି। ଆପଣ ଦେଖିବାକୁ ଚାହୁଁଥିବାଟି ବାଛନ୍ତୁ।",
337
+ "contactLinkedToMultipleProfiles": "ଏହି ଯୋଗାଯୋଗ ଏକାଧିକ ପ୍ରୋଫାଇଲ୍ ସହିତ ଯୋଡ଼ାଯାଇଛି। ଆପଣ ଦେଖିବାକୁ ଚାହୁଁଥିବାଟି ବାଛନ୍ତୁ।",
338
+ "signingInEllipsis": "ସାଇନ୍ ଇନ୍ ହେଉଛି…",
339
+ "couldNotSwitchProfile": "ସେହି ପ୍ରୋଫାଇଲକୁ ସ୍ୱିଚ୍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁନର୍ବାର ଚେଷ୍ଟା କରନ୍ତୁ।"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "ਪ੍ਰੋਫਾਈਲ ਬਦਲੋ",
335
+ "chooseYourProfile": "ਆਪਣੀ ਪ੍ਰੋਫਾਈਲ ਚੁਣੋ",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ਇੱਕ ਤੋਂ ਵੱਧ ਪ੍ਰੋਫਾਈਲਾਂ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ। ਜੋ ਤੁਸੀਂ ਵੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ ਉਹ ਚੁਣੋ।",
337
+ "contactLinkedToMultipleProfiles": "ਇਹ ਸੰਪਰਕ ਇੱਕ ਤੋਂ ਵੱਧ ਪ੍ਰੋਫਾਈਲਾਂ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ। ਜੋ ਤੁਸੀਂ ਵੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ ਉਹ ਚੁਣੋ।",
338
+ "signingInEllipsis": "ਸਾਈਨ ਇਨ ਹੋ ਰਿਹਾ ਹੈ…",
339
+ "couldNotSwitchProfile": "ਉਸ ਪ੍ਰੋਫਾਈਲ 'ਤੇ ਸਵਿੱਚ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "प्रोफ़ाइलं परिवर्तयतु",
335
+ "chooseYourProfile": "स्वप्रोफ़ाइलं चिनोतु",
336
+ "emailLinkedToMultipleProfiles": "{{email}} एकाधिकैः प्रोफ़ाइलैः सह सम्बद्धः अस्ति। यत् द्रष्टुम् इच्छति तत् चिनोतु।",
337
+ "contactLinkedToMultipleProfiles": "अयं सम्पर्कः एकाधिकैः प्रोफ़ाइलैः सह सम्बद्धः अस्ति। यत् द्रष्टुम् इच्छति तत् चिनोतु।",
338
+ "signingInEllipsis": "प्रवेशः क्रियते…",
339
+ "couldNotSwitchProfile": "तां प्रोफ़ाइलं प्रति परिवर्तनं न शक्यते स्म। कृपया पुनः प्रयत्नं करोतु।"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "ᱯᱨᱳᱯᱷᱟᱭᱤᱞ ᱵᱚᱫᱚᱞ ᱢᱮ",
335
+ "chooseYourProfile": "ᱟᱢᱟᱜ ᱯᱨᱳᱯᱷᱟᱭᱤᱞ ᱵᱟᱪᱷᱟᱣ ᱢᱮ",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ᱫᱚ ᱢᱤᱫ ᱠᱷᱚᱱ ᱰᱷᱮᱨ ᱯᱨᱳᱯᱷᱟᱭᱤᱞ ᱥᱟᱶ ᱡᱩᱰᱟᱹᱣ ᱟᱠᱟᱱᱟ᱾ ᱟᱢ ᱧᱮᱞ ᱥᱟᱱᱟᱢ ᱚᱱᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ᱾",
337
+ "contactLinkedToMultipleProfiles": "ᱱᱳᱶᱟ ᱠᱚᱱᱴᱮᱠᱴ ᱫᱚ ᱢᱤᱫ ᱠᱷᱚᱱ ᱰᱷᱮᱨ ᱯᱨᱳᱯᱷᱟᱭᱤᱞ ᱥᱟᱶ ᱡᱩᱰᱟᱹᱣ ᱟᱠᱟᱱᱟ᱾ ᱟᱢ ᱧᱮᱞ ᱥᱟᱱᱟᱢ ᱚᱱᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ᱾",
338
+ "signingInEllipsis": "ᱥᱟᱭᱤᱱ ᱤᱱ ᱮᱫᱟᱭ…",
339
+ "couldNotSwitchProfile": "ᱚᱱᱟ ᱯᱨᱳᱯᱷᱟᱭᱤᱞ ᱛᱮ ᱥᱩᱭᱪ ᱵᱟᱝ ᱦᱩᱭ ᱞᱮᱱᱟ᱾ ᱫᱟᱭᱟ ᱠᱟᱛᱮ ᱫᱩᱠᱟᱹᱛᱮ ᱠᱩᱨᱩᱢᱩᱴᱩ ᱢᱮ᱾"
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "پروفائل تبديل ڪريو",
335
+ "chooseYourProfile": "پنهنجو پروفائل چونڊيو",
336
+ "emailLinkedToMultipleProfiles": "{{email}} هڪ کان وڌيڪ پروفائلن سان ڳنڍيل آهي. جيڪو توهان ڏسڻ چاهيو ٿا اهو چونڊيو.",
337
+ "contactLinkedToMultipleProfiles": "هي رابطو هڪ کان وڌيڪ پروفائلن سان ڳنڍيل آهي. جيڪو توهان ڏسڻ چاهيو ٿا اهو چونڊيو.",
338
+ "signingInEllipsis": "سائن ان ٿي رهيو آهي…",
339
+ "couldNotSwitchProfile": "ان پروفائل تي سوئچ نه ٿي سگهيو. مهرباني ڪري ٻيهر ڪوشش ڪريو."
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "சுயவிவரத்தை மாற்று",
335
+ "chooseYourProfile": "உங்கள் சுயவிவரத்தைத் தேர்ந்தெடுக்கவும்",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ஒன்றுக்கு மேற்பட்ட சுயவிவரங்களுடன் இணைக்கப்பட்டுள்ளது. நீங்கள் பார்க்க விரும்புவதைத் தேர்ந்தெடுக்கவும்.",
337
+ "contactLinkedToMultipleProfiles": "இந்தத் தொடர்பு ஒன்றுக்கு மேற்பட்ட சுயவிவரங்களுடன் இணைக்கப்பட்டுள்ளது. நீங்கள் பார்க்க விரும்புவதைத் தேர்ந்தெடுக்கவும்.",
338
+ "signingInEllipsis": "உள்நுழைகிறது…",
339
+ "couldNotSwitchProfile": "அந்தச் சுயவிவரத்திற்கு மாற முடியவில்லை. மீண்டும் முயற்சிக்கவும்."
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "ప్రొఫైల్‌ను మార్చండి",
335
+ "chooseYourProfile": "మీ ప్రొఫైల్‌ను ఎంచుకోండి",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ఒకటి కంటే ఎక్కువ ప్రొఫైల్‌లతో అనుసంధానించబడింది. మీరు చూడాలనుకుంటున్నదాన్ని ఎంచుకోండి.",
337
+ "contactLinkedToMultipleProfiles": "ఈ సంప్రదింపు ఒకటి కంటే ఎక్కువ ప్రొఫైల్‌లతో అనుసంధానించబడింది. మీరు చూడాలనుకుంటున్నదాన్ని ఎంచుకోండి.",
338
+ "signingInEllipsis": "సైన్ ఇన్ అవుతోంది…",
339
+ "couldNotSwitchProfile": "ఆ ప్రొఫైల్‌కు మారడం సాధ్యం కాలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."
334
340
  }
@@ -330,5 +330,11 @@
330
330
  "casesCreatedCount_other": "{{count}} cases created",
331
331
  "someRequestsFailed": "Some requests failed",
332
332
  "retry": "Retry",
333
- "retrying": "Retrying..."
333
+ "retrying": "Retrying...",
334
+ "switchProfile": "پروفائل تبدیل کریں",
335
+ "chooseYourProfile": "اپنی پروفائل منتخب کریں",
336
+ "emailLinkedToMultipleProfiles": "{{email}} ایک سے زیادہ پروفائلز سے منسلک ہے۔ جسے آپ دیکھنا چاہتے ہیں اسے منتخب کریں۔",
337
+ "contactLinkedToMultipleProfiles": "یہ رابطہ ایک سے زیادہ پروفائلز سے منسلک ہے۔ جسے آپ دیکھنا چاہتے ہیں اسے منتخب کریں۔",
338
+ "signingInEllipsis": "سائن ان ہو رہا ہے…",
339
+ "couldNotSwitchProfile": "اس پروفائل پر سوئچ نہیں کیا جا سکا۔ براہ کرم دوبارہ کوشش کریں۔"
334
340
  }
@@ -664,6 +664,111 @@ export const injectPCStyles = () => {
664
664
  color: var(--pc-text-secondary);
665
665
  line-height: 1.25;
666
666
  }
667
+ .pc-select-user {
668
+ display: flex;
669
+ flex-direction: column;
670
+ justify-content: center;
671
+ gap: var(--pc-spacing-xl);
672
+ width: 100%;
673
+ max-width: 460px;
674
+ margin: 0 auto;
675
+ min-height: min(62vh, 540px);
676
+ padding-top: var(--pc-spacing-2xl);
677
+ padding-bottom: var(--pc-spacing-2xl);
678
+ }
679
+ .pc-select-user__header {
680
+ display: flex;
681
+ flex-direction: column;
682
+ align-items: center;
683
+ text-align: center;
684
+ gap: var(--pc-spacing-sm);
685
+ }
686
+ .pc-select-user__header > * {
687
+ margin: 0;
688
+ }
689
+ .pc-select-user__list {
690
+ display: flex;
691
+ flex-direction: column;
692
+ gap: 0.625rem;
693
+ }
694
+ .pc-select-user__item {
695
+ display: flex;
696
+ align-items: center;
697
+ justify-content: space-between;
698
+ gap: 0.75rem;
699
+ width: 100%;
700
+ box-sizing: border-box;
701
+ padding: 0.75rem 1rem;
702
+ border: 1px solid var(--pc-border-color);
703
+ border-radius: 0.625rem;
704
+ background-color: var(--pc-bg-primary);
705
+ color: var(--pc-text-primary);
706
+ cursor: pointer;
707
+ text-align: left;
708
+ appearance: none;
709
+ -webkit-appearance: none;
710
+ font-family: inherit;
711
+ transition: border-color 0.2s ease, background-color 0.2s ease;
712
+ }
713
+ .pc-select-user__item:hover:not(:disabled) {
714
+ border-color: var(--pc-primary);
715
+ background-color: color-mix(in srgb, var(--pc-primary) 6%, var(--pc-bg-primary));
716
+ }
717
+ .pc-select-user__item:disabled {
718
+ cursor: default;
719
+ opacity: 0.7;
720
+ }
721
+ .pc-select-user__item:focus-visible {
722
+ outline: 2px solid var(--pc-border-color-focus);
723
+ outline-offset: 2px;
724
+ }
725
+ .pc-select-user__item-text {
726
+ display: flex;
727
+ flex-direction: column;
728
+ align-items: flex-start;
729
+ gap: 0.125rem;
730
+ min-width: 0;
731
+ }
732
+ .pc-select-user__item-primary {
733
+ font-size: 0.875rem;
734
+ font-weight: 600;
735
+ line-height: 1.25;
736
+ overflow: hidden;
737
+ text-overflow: ellipsis;
738
+ white-space: nowrap;
739
+ max-width: 100%;
740
+ }
741
+ .pc-select-user__item-secondary {
742
+ font-size: 0.75rem;
743
+ color: var(--pc-text-secondary);
744
+ line-height: 1.25;
745
+ overflow: hidden;
746
+ text-overflow: ellipsis;
747
+ white-space: nowrap;
748
+ max-width: 100%;
749
+ }
750
+ .pc-select-user__item-status {
751
+ font-size: 0.75rem;
752
+ font-weight: 600;
753
+ color: var(--pc-primary);
754
+ flex-shrink: 0;
755
+ }
756
+ .pc-select-user__item-chevron {
757
+ color: var(--pc-primary);
758
+ flex-shrink: 0;
759
+ }
760
+ .pc-select-user__error {
761
+ margin: 0.75rem 0 0;
762
+ font-size: 0.8125rem;
763
+ color: var(--pc-error);
764
+ text-align: center;
765
+ }
766
+ .pc-select-user-loading {
767
+ display: flex;
768
+ align-items: center;
769
+ justify-content: center;
770
+ min-height: 60vh;
771
+ }
667
772
  .pc-product-purpose-panel {
668
773
  display: flex;
669
774
  flex-direction: column;
@@ -98,9 +98,14 @@ vi.mock('../src/RedactoPrivacyCenter/lib/i18n', () => ({
98
98
  // 1-form -> 1-case: only `createCaseRequest` is consumed by Form.tsx now.
99
99
  const createCaseRequest = vi.fn();
100
100
  const getFormData = vi.fn();
101
+ const fetchUserConsents = vi.fn();
101
102
  vi.mock('../src/RedactoPrivacyCenter/lib/api', () => ({
102
103
  createCaseRequest: (...args: unknown[]) => createCaseRequest(...args),
103
104
  getFormData: (...args: unknown[]) => getFormData(...args),
105
+ // Approach A sources the product picker from the ledger's user-consents.
106
+ // Default to no ledger result so the form falls back to the getFormData
107
+ // product groups these tests set up.
108
+ fetchUserConsents: (...args: unknown[]) => fetchUserConsents(...args),
104
109
  }));
105
110
 
106
111
  // ─── upload stub ─────────────────────────────────────────────────────────────
@@ -135,13 +140,16 @@ vi.mock('../src/RedactoPrivacyCenter/context/AuthContext', () => {
135
140
  return { useAuth: () => auth };
136
141
  });
137
142
 
138
- vi.mock('../src/RedactoPrivacyCenter/context/BaseUrlContext', () => {
139
- const base = {
140
- consentClient: { get: () => Promise.resolve(null), post: () => Promise.resolve(null) },
141
- baseUrl: 'https://consent.test',
142
- };
143
- return { useBaseUrl: () => base };
144
- });
143
+ // Mutable so individual tests can drop `ledgerBaseUrl` to exercise the legacy
144
+ // (no-ledger) mode; beforeEach restores the ledger default.
145
+ const baseUrlStub = vi.hoisted(() => ({
146
+ consentClient: { get: () => Promise.resolve(null), post: () => Promise.resolve(null) },
147
+ baseUrl: 'https://consent.test',
148
+ ledgerBaseUrl: 'https://ledger.test/consent-ledger' as string | undefined,
149
+ }));
150
+ vi.mock('../src/RedactoPrivacyCenter/context/BaseUrlContext', () => ({
151
+ useBaseUrl: () => baseUrlStub,
152
+ }));
145
153
 
146
154
  vi.mock('../src/RedactoPrivacyCenter/context/OrganizationContext', () => {
147
155
  const org = {
@@ -476,6 +484,8 @@ describe('Form (P2-SDK-3)', () => {
476
484
  beforeEach(() => {
477
485
  createCaseRequest.mockReset();
478
486
  getFormData.mockReset();
487
+ fetchUserConsents.mockReset();
488
+ baseUrlStub.ledgerBaseUrl = 'https://ledger.test/consent-ledger';
479
489
  sessionStorage.clear();
480
490
  });
481
491
 
@@ -514,6 +524,47 @@ describe('Form (P2-SDK-3)', () => {
514
524
  expect(submitButton.disabled).toBe(true);
515
525
  });
516
526
 
527
+ it('skips the ledger picker overlay when no ledgerBaseUrl is configured', async () => {
528
+ // Without a ledger, consents still live in Python and the form/data
529
+ // groups are authoritative — the overlay must not fire a user-consents
530
+ // read (it would hit the legacy endpoint and mis-map its shape).
531
+ baseUrlStub.ledgerBaseUrl = undefined;
532
+ setupSession();
533
+ getFormData.mockResolvedValue(
534
+ makeFormData({
535
+ direct_groups: [
536
+ makeGroup(),
537
+ makeGroup({
538
+ product_uuid: 'prod-direct-2',
539
+ product_name: 'Acme Rewards',
540
+ purposes: [basePurpose('purp-2', 'Analytics')],
541
+ }),
542
+ ],
543
+ }),
544
+ );
545
+
546
+ render(<Form onCreateRequest={vi.fn()} />);
547
+ await waitFor(() => expect(getFormData).toHaveBeenCalled());
548
+
549
+ pickRequestType(RequestTypeEnum.ERASURE);
550
+ await waitFor(() => {
551
+ expect(screen.getByText('Acme Loyalty')).toBeInTheDocument();
552
+ });
553
+ expect(fetchUserConsents).not.toHaveBeenCalled();
554
+ });
555
+
556
+ it('runs the ledger picker overlay when ledgerBaseUrl is configured', async () => {
557
+ setupSession();
558
+ getFormData.mockResolvedValue(
559
+ makeFormData({ direct_groups: [makeGroup()] }),
560
+ );
561
+
562
+ render(<Form onCreateRequest={vi.fn()} />);
563
+ await waitFor(() => expect(getFormData).toHaveBeenCalled());
564
+
565
+ await waitFor(() => expect(fetchUserConsents).toHaveBeenCalled());
566
+ });
567
+
517
568
  it('renders the product step for DSAR types when direct_groups is present', async () => {
518
569
  setupSession();
519
570
  const direct = makeGroup({
@@ -0,0 +1,171 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ import { createConsentClient } from '../src/RedactoPrivacyCenter/api/client';
4
+ import type { BaseRequest } from '../src/RedactoPrivacyCenter/api/client';
5
+
6
+ // URL routing contract for the five consent read/write methods that moved to
7
+ // the Go ledger in PR #438. With a ledgerBaseUrl they must hit the ledger's
8
+ // /public/... paths; without one they must fall back to the legacy Python
9
+ // /dsar/privacy-center paths (prod has no ledger — see PR #438/#460 regression
10
+ // where the silent baseUrl fallback 404'd the Privacy Center).
11
+
12
+ const BASE_URL = 'https://consent.test';
13
+ const LEDGER_URL = 'https://ledger.test/consent-ledger';
14
+ const LEGACY_PREFIX =
15
+ 'https://consent.test/organisations/org-1/workspaces/ws-1/dsar/privacy-center';
16
+ const LEDGER_PREFIX =
17
+ 'https://ledger.test/consent-ledger/public/organisations/org-1/workspaces/ws-1';
18
+
19
+ const baseRequest: BaseRequest = {
20
+ organisationUuid: 'org-1',
21
+ workspaceUuid: 'ws-1',
22
+ authToken: 'jwt-token',
23
+ };
24
+
25
+ const jsonResponse = () =>
26
+ new Response(JSON.stringify({}), {
27
+ status: 200,
28
+ headers: { 'Content-Type': 'application/json' },
29
+ });
30
+
31
+ const pdfResponse = () =>
32
+ new Response(new Blob(['%PDF'], { type: 'application/pdf' }), {
33
+ status: 200,
34
+ headers: { 'Content-Type': 'application/pdf' },
35
+ });
36
+
37
+ const mockFetch = vi.fn();
38
+
39
+ const fetchedUrl = (): string => String(mockFetch.mock.calls[0]?.[0]);
40
+
41
+ const fetchedBody = (): Record<string, unknown> =>
42
+ JSON.parse(String(mockFetch.mock.calls[0]?.[1]?.body)) as Record<
43
+ string,
44
+ unknown
45
+ >;
46
+
47
+ beforeEach(() => {
48
+ mockFetch.mockReset();
49
+ mockFetch.mockImplementation(() => Promise.resolve(jsonResponse()));
50
+ vi.stubGlobal('fetch', mockFetch);
51
+ });
52
+
53
+ afterEach(() => {
54
+ vi.unstubAllGlobals();
55
+ });
56
+
57
+ describe('createConsentClient — legacy mode (no ledgerBaseUrl)', () => {
58
+ const client = createConsentClient(BASE_URL);
59
+
60
+ it('routes getUserConsents to the legacy dsar/privacy-center path', async () => {
61
+ await client.getUserConsents(baseRequest, { offset: 0, limit: 10 });
62
+ expect(fetchedUrl()).toBe(
63
+ `${LEGACY_PREFIX}/user-consents?offset=0&limit=10`,
64
+ );
65
+ });
66
+
67
+ it('routes getGroupPurposes to the legacy dsar/privacy-center path', async () => {
68
+ await client.getGroupPurposes(baseRequest, 'prod-1', { limit: 5 });
69
+ expect(fetchedUrl()).toBe(
70
+ `${LEGACY_PREFIX}/user-consents/prod-1/purposes?limit=5`,
71
+ );
72
+ });
73
+
74
+ it('routes manageConsent to the legacy manage-consent endpoint with the legacy body and no noticeUuid requirement', async () => {
75
+ await client.manageConsent(
76
+ baseRequest,
77
+ 'purp-1',
78
+ 'jane@example.com',
79
+ 'revoke',
80
+ undefined,
81
+ undefined,
82
+ 'nominator@example.com',
83
+ ['de-1'],
84
+ 'prod-1',
85
+ );
86
+ expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/manage-consent?status=revoke`);
87
+ expect(fetchedBody()).toEqual({
88
+ purpose_uuid: 'purp-1',
89
+ contact: 'jane@example.com',
90
+ nominator_contact: 'nominator@example.com',
91
+ data_element_uuids: ['de-1'],
92
+ product_uuid: 'prod-1',
93
+ });
94
+ });
95
+
96
+ it('routes getReceipts to the legacy dsar/privacy-center path', async () => {
97
+ await client.getReceipts(baseRequest, { skip: 0, limit: 10 });
98
+ expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/receipts?skip=0&limit=10`);
99
+ });
100
+
101
+ it('routes getReceiptPdf to the legacy /pdf path', async () => {
102
+ mockFetch.mockImplementation(() => Promise.resolve(pdfResponse()));
103
+ await client.getReceiptPdf(baseRequest, 'r-1');
104
+ expect(fetchedUrl()).toBe(`${LEGACY_PREFIX}/receipts/r-1/pdf`);
105
+ });
106
+ });
107
+
108
+ describe('createConsentClient — ledger mode (ledgerBaseUrl set)', () => {
109
+ // Trailing slash must be trimmed from the configured ledger base.
110
+ const client = createConsentClient(BASE_URL, `${LEDGER_URL}/`);
111
+
112
+ it('routes getUserConsents to the ledger public path', async () => {
113
+ await client.getUserConsents(baseRequest, { offset: 0, limit: 10 });
114
+ expect(fetchedUrl()).toBe(
115
+ `${LEDGER_PREFIX}/user-consents?offset=0&limit=10`,
116
+ );
117
+ });
118
+
119
+ it('routes getGroupPurposes to the ledger public path', async () => {
120
+ await client.getGroupPurposes(baseRequest, 'prod-1', { limit: 5 });
121
+ expect(fetchedUrl()).toBe(
122
+ `${LEDGER_PREFIX}/user-consents/prod-1/purposes?limit=5`,
123
+ );
124
+ });
125
+
126
+ it('routes manageConsent to the ledger submit-consent with a partial payload', async () => {
127
+ await client.manageConsent(
128
+ baseRequest,
129
+ 'purp-1',
130
+ 'jane@example.com',
131
+ 'revoke',
132
+ undefined,
133
+ undefined,
134
+ undefined,
135
+ undefined,
136
+ undefined,
137
+ 'notice-1',
138
+ [{ uuid: 'de-1', selected: true }],
139
+ );
140
+ expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/submit-consent`);
141
+ expect(fetchedBody()).toMatchObject({
142
+ notice_uuid: 'notice-1',
143
+ partial: true,
144
+ purposes: [
145
+ {
146
+ purpose_uuid: 'purp-1',
147
+ selected: false,
148
+ data_elements: [{ uuid: 'de-1', selected: false }],
149
+ },
150
+ ],
151
+ });
152
+ });
153
+
154
+ it('throws from manageConsent when noticeUuid is missing', async () => {
155
+ await expect(
156
+ client.manageConsent(baseRequest, 'purp-1', 'jane@example.com', 'revoke'),
157
+ ).rejects.toThrow('notice_uuid is required');
158
+ expect(mockFetch).not.toHaveBeenCalled();
159
+ });
160
+
161
+ it('routes getReceipts to the ledger public path', async () => {
162
+ await client.getReceipts(baseRequest, { skip: 0, limit: 10 });
163
+ expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/receipts?skip=0&limit=10`);
164
+ });
165
+
166
+ it('routes getReceiptPdf to the ledger /download path', async () => {
167
+ mockFetch.mockImplementation(() => Promise.resolve(pdfResponse()));
168
+ await client.getReceiptPdf(baseRequest, 'r-1');
169
+ expect(fetchedUrl()).toBe(`${LEDGER_PREFIX}/receipts/r-1/download`);
170
+ });
171
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import { manageConsentServer } from '../src/RedactoPrivacyCenter/api/actions';
4
+ import type { createConsentClient } from '../src/RedactoPrivacyCenter/api/client';
5
+
6
+ // manageConsentServer must honor the legacy Python response's `success` flag
7
+ // (a 200 with { success: false } is a failure) while still treating the Go
8
+ // ledger's ConsentEventResponse (no `success` key) as resolved-on-2xx.
9
+
10
+ type ConsentClient = ReturnType<typeof createConsentClient>;
11
+
12
+ const makeParams = (result: unknown) => ({
13
+ organizationId: 'org-1',
14
+ workspaceId: 'ws-1',
15
+ purposeId: 'purp-1',
16
+ contact: 'jane@example.com',
17
+ action: 'revoke' as const,
18
+ authToken: 'jwt-token',
19
+ consentClient: {
20
+ manageConsent: vi.fn().mockResolvedValue(result),
21
+ } as unknown as ConsentClient,
22
+ });
23
+
24
+ describe('manageConsentServer success detection', () => {
25
+ it('returns false for a legacy { success: false } response', async () => {
26
+ await expect(manageConsentServer(makeParams({ success: false }))).resolves.toBe(false);
27
+ });
28
+
29
+ it('returns true for a legacy { success: true } response', async () => {
30
+ await expect(manageConsentServer(makeParams({ success: true }))).resolves.toBe(true);
31
+ });
32
+
33
+ it('returns true for a ledger ConsentEventResponse (no success flag)', async () => {
34
+ await expect(
35
+ manageConsentServer(makeParams({ consent_event_uuid: 'evt-1' })),
36
+ ).resolves.toBe(true);
37
+ });
38
+ });
package/tests/mocks.ts CHANGED
@@ -235,6 +235,7 @@ export const defaultProps = {
235
235
  org_uuid: "org-123",
236
236
  workspace_uuid: "workspace-123",
237
237
  notice_uuid: "notice-123",
238
+ ledgerBaseUrl: "https://ledger.test/consent-ledger",
238
239
  accessToken: "mock-token",
239
240
  refreshToken: "mock-refresh-token",
240
241
  language: "en",