@redacto.io/consent-sdk-react 6.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/.turbo/turbo-build.log +25 -24
  2. package/CHANGELOG.md +23 -0
  3. package/dist/index.js +228 -166
  4. package/dist/index.mjs +228 -166
  5. package/dist/privacy-center.d.mts +2 -4
  6. package/dist/privacy-center.d.ts +2 -4
  7. package/dist/privacy-center.js +1876 -2079
  8. package/dist/privacy-center.mjs +1470 -1679
  9. package/package.json +1 -1
  10. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +209 -155
  11. package/src/RedactoNoticeConsent/styles.ts +10 -1
  12. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +6 -10
  13. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +18 -17
  14. package/src/RedactoPrivacyCenter/api/orgBranding.ts +9 -0
  15. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +16 -14
  16. package/src/RedactoPrivacyCenter/components/Form.tsx +3 -3
  17. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +1 -1
  18. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +0 -4
  19. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +16 -2
  20. package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +44 -0
  21. package/src/RedactoPrivacyCenter/lib/types.ts +22 -0
  22. package/src/RedactoPrivacyCenter/styles/applyBrandingTheme.ts +66 -0
  23. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +3 -111
  24. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +3 -2
  25. package/src/RedactoPrivacyCenter/styles/variables.css +1 -43
  26. package/src/RedactoPrivacyCenter/ui/Footer.tsx +4 -10
  27. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +0 -85
  28. package/src/RedactoPrivacyCenter/context/theme.tsx +0 -127
  29. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +0 -70
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redacto.io/consent-sdk-react",
3
- "version": "6.0.0",
3
+ "version": "7.1.0",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -1511,6 +1511,15 @@ export const RedactoNoticeConsent = ({
1511
1511
  height: isMobile ? "auto" : undefined,
1512
1512
  width: isMobile ? "auto" : undefined,
1513
1513
  },
1514
+ topSection: {
1515
+ flexDirection: isMobile ? ("column" as const) : ("row" as const),
1516
+ alignItems: isMobile ? ("stretch" as const) : ("flex-start" as const),
1517
+ gap: isMobile ? "12px" : "10px",
1518
+ },
1519
+ logo: {
1520
+ maxWidth: isMobile ? "96px" : "140px",
1521
+ maxHeight: isMobile ? "28px" : "32px",
1522
+ },
1514
1523
  languageItem: {
1515
1524
  fontSize: isMobile ? "11px" : "12px",
1516
1525
  padding: isMobile ? "6px 10px" : "8px 12px",
@@ -3663,176 +3672,221 @@ export const RedactoNoticeConsent = ({
3663
3672
  </button>
3664
3673
  </div>
3665
3674
  )}
3666
- <div style={{ ...styles.topSection, ...sectionStyle }}>
3667
- <div style={styles.topLeft}>
3668
- <img
3669
- style={styles.logo}
3670
- src={content?.logo_url || logo}
3671
- alt="Redacto Logo"
3672
- />
3673
- <h2
3674
- key={`privacy-notice-title-${selectedLanguage}`}
3675
- id="privacy-notice-title"
3676
- style={{
3677
- ...styles.title,
3678
- ...responsiveStyles.title,
3679
- ...componentHeadingStyle,
3680
- }}
3681
- >
3682
- {getTranslatedText(
3683
- "notice_banner_heading",
3684
- content?.notice_banner_heading || "Privacy Notice"
3685
- )}
3686
- </h2>
3687
- </div>
3688
- <div style={styles.languageSelectorContainer}>
3689
- <div
3690
- style={{
3691
- display: "flex",
3692
- alignItems: "center",
3693
- gap: "8px",
3694
- }}
3695
- >
3696
- {/* Speaker icon button - only visible when TTS is available */}
3697
- {isTTSAvailable && (
3698
- <button
3699
- onClick={toggleAudio}
3675
+ <div
3676
+ style={{
3677
+ ...styles.topSection,
3678
+ ...responsiveStyles.topSection,
3679
+ ...sectionStyle,
3680
+ }}
3681
+ >
3682
+ {(() => {
3683
+ const logoNode = (
3684
+ <img
3685
+ style={{ ...styles.logo, ...responsiveStyles.logo }}
3686
+ src={content?.logo_url || logo}
3687
+ alt="Redacto Logo"
3688
+ />
3689
+ );
3690
+ const titleNode = (
3691
+ <h2
3692
+ key={`privacy-notice-title-${selectedLanguage}`}
3693
+ id="privacy-notice-title"
3694
+ style={{
3695
+ ...styles.title,
3696
+ ...responsiveStyles.title,
3697
+ ...componentHeadingStyle,
3698
+ }}
3699
+ >
3700
+ {getTranslatedText(
3701
+ "notice_banner_heading",
3702
+ content?.notice_banner_heading || "Privacy Notice"
3703
+ )}
3704
+ </h2>
3705
+ );
3706
+ const controlsBlock = (
3707
+ <div style={styles.languageSelectorContainer}>
3708
+ <div
3700
3709
  style={{
3701
- ...styles.topRight,
3702
- ...responsiveStyles.topRight,
3703
- ...languageSelectorStyle,
3704
- padding: "3px 9px",
3705
3710
  display: "flex",
3706
3711
  alignItems: "center",
3707
- justifyContent: "center",
3712
+ gap: "8px",
3708
3713
  }}
3709
- aria-label={
3710
- isPlaying && !isPaused
3711
- ? "Pause audio playback"
3712
- : isPaused
3713
- ? "Resume audio playback"
3714
- : "Start audio playback"
3715
- }
3716
- title={
3717
- isPlaying && !isPaused
3718
- ? "Pause Talkback"
3719
- : isPaused
3720
- ? "Resume Talkback"
3721
- : "Start Talkback"
3722
- }
3723
3714
  >
3724
- {isPlaying && !isPaused ? (
3725
- // Pause icon
3726
- <svg
3727
- xmlns="http://www.w3.org/2000/svg"
3728
- width="16"
3729
- height="16"
3730
- viewBox="0 0 24 24"
3731
- fill="none"
3732
- stroke={
3733
- settings?.button?.language?.textColor ||
3734
- "currentColor"
3715
+ {/* Speaker icon button - only visible when TTS is available */}
3716
+ {isTTSAvailable && (
3717
+ <button
3718
+ onClick={toggleAudio}
3719
+ style={{
3720
+ ...styles.topRight,
3721
+ ...responsiveStyles.topRight,
3722
+ ...languageSelectorStyle,
3723
+ padding: "3px 9px",
3724
+ display: "flex",
3725
+ alignItems: "center",
3726
+ justifyContent: "center",
3727
+ }}
3728
+ aria-label={
3729
+ isPlaying && !isPaused
3730
+ ? "Pause audio playback"
3731
+ : isPaused
3732
+ ? "Resume audio playback"
3733
+ : "Start audio playback"
3735
3734
  }
3736
- strokeWidth="2"
3737
- strokeLinecap="round"
3738
- strokeLinejoin="round"
3739
- aria-hidden="true"
3740
- >
3741
- <rect x="6" y="4" width="4" height="16" />
3742
- <rect x="14" y="4" width="4" height="16" />
3743
- </svg>
3744
- ) : (
3745
- // Play/Sound icon
3746
- <svg
3747
- xmlns="http://www.w3.org/2000/svg"
3748
- width="16"
3749
- height="16"
3750
- viewBox="0 0 24 24"
3751
- fill="none"
3752
- stroke={
3753
- settings?.button?.language?.textColor ||
3754
- "currentColor"
3735
+ title={
3736
+ isPlaying && !isPaused
3737
+ ? "Pause Talkback"
3738
+ : isPaused
3739
+ ? "Resume Talkback"
3740
+ : "Start Talkback"
3755
3741
  }
3756
- strokeWidth="2"
3757
- strokeLinecap="round"
3758
- strokeLinejoin="round"
3759
- aria-hidden="true"
3760
3742
  >
3761
- <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
3762
- <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" />
3763
- </svg>
3743
+ {isPlaying && !isPaused ? (
3744
+ // Pause icon
3745
+ <svg
3746
+ xmlns="http://www.w3.org/2000/svg"
3747
+ width="16"
3748
+ height="16"
3749
+ viewBox="0 0 24 24"
3750
+ fill="none"
3751
+ stroke={
3752
+ settings?.button?.language?.textColor ||
3753
+ "currentColor"
3754
+ }
3755
+ strokeWidth="2"
3756
+ strokeLinecap="round"
3757
+ strokeLinejoin="round"
3758
+ aria-hidden="true"
3759
+ >
3760
+ <rect x="6" y="4" width="4" height="16" />
3761
+ <rect x="14" y="4" width="4" height="16" />
3762
+ </svg>
3763
+ ) : (
3764
+ // Play/Sound icon
3765
+ <svg
3766
+ xmlns="http://www.w3.org/2000/svg"
3767
+ width="16"
3768
+ height="16"
3769
+ viewBox="0 0 24 24"
3770
+ fill="none"
3771
+ stroke={
3772
+ settings?.button?.language?.textColor ||
3773
+ "currentColor"
3774
+ }
3775
+ strokeWidth="2"
3776
+ strokeLinecap="round"
3777
+ strokeLinejoin="round"
3778
+ aria-hidden="true"
3779
+ >
3780
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
3781
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" />
3782
+ </svg>
3783
+ )}
3784
+ </button>
3764
3785
  )}
3765
- </button>
3766
- )}
3767
- <div>
3768
- <button
3769
- ref={firstFocusableRef}
3786
+ <div>
3787
+ <button
3788
+ ref={firstFocusableRef}
3789
+ style={{
3790
+ ...styles.topRight,
3791
+ ...responsiveStyles.topRight,
3792
+ ...languageSelectorStyle,
3793
+ }}
3794
+ onClick={toggleLanguageDropdown}
3795
+ aria-expanded={isLanguageDropdownOpen}
3796
+ aria-haspopup="listbox"
3797
+ aria-controls="language-listbox"
3798
+ aria-label={`Select language. Current language: ${toNativeLabel(selectedLanguage)}`}
3799
+ >
3800
+ {toNativeLabel(selectedLanguage)}
3801
+ <svg
3802
+ xmlns="http://www.w3.org/2000/svg"
3803
+ width="16"
3804
+ height="16"
3805
+ viewBox="0 0 16 16"
3806
+ fill="none"
3807
+ stroke={
3808
+ settings?.button?.language?.textColor ||
3809
+ "currentColor"
3810
+ }
3811
+ strokeWidth="2"
3812
+ strokeLinecap="round"
3813
+ strokeLinejoin="round"
3814
+ style={{ flexShrink: 0, marginLeft: "4px" }}
3815
+ aria-hidden="true"
3816
+ >
3817
+ <path d="M4 6l4 4 4-4" />
3818
+ </svg>
3819
+ </button>
3820
+ </div>
3821
+ </div>
3822
+ {isLanguageDropdownOpen && (
3823
+ <ul
3824
+ id="language-listbox"
3825
+ style={{
3826
+ ...styles.languageDropdown,
3827
+ ...languageSelectorStyle,
3828
+ }}
3829
+ role="listbox"
3830
+ aria-label="Select language"
3831
+ >
3832
+ {availableLanguages.map((lang: string) => (
3833
+ <li
3834
+ key={lang}
3835
+ style={{
3836
+ ...styles.languageItem,
3837
+ ...responsiveStyles.languageItem,
3838
+ ...(selectedLanguage === lang
3839
+ ? selectedLanguageItemStyle
3840
+ : languageSelectorStyle),
3841
+ listStyle: "none",
3842
+ }}
3843
+ onClick={() => handleLanguageSelect(lang)}
3844
+ role="option"
3845
+ aria-selected={selectedLanguage === lang}
3846
+ tabIndex={0}
3847
+ aria-label={`Select ${toNativeLabel(lang)} language`}
3848
+ >
3849
+ {toNativeLabel(lang)}
3850
+ </li>
3851
+ ))}
3852
+ </ul>
3853
+ )}
3854
+ </div>
3855
+ );
3856
+ return isMobile ? (
3857
+ <>
3858
+ <div
3770
3859
  style={{
3771
- ...styles.topRight,
3772
- ...responsiveStyles.topRight,
3773
- ...languageSelectorStyle,
3860
+ display: "flex",
3861
+ alignItems: "center",
3862
+ gap: "10px",
3863
+ width: "100%",
3774
3864
  }}
3775
- onClick={toggleLanguageDropdown}
3776
- aria-expanded={isLanguageDropdownOpen}
3777
- aria-haspopup="listbox"
3778
- aria-controls="language-listbox"
3779
- aria-label={`Select language. Current language: ${toNativeLabel(selectedLanguage)}`}
3780
3865
  >
3781
- {toNativeLabel(selectedLanguage)}
3782
- <svg
3783
- xmlns="http://www.w3.org/2000/svg"
3784
- width="16"
3785
- height="16"
3786
- viewBox="0 0 16 16"
3787
- fill="none"
3788
- stroke={
3789
- settings?.button?.language?.textColor ||
3790
- "currentColor"
3791
- }
3792
- strokeWidth="2"
3793
- strokeLinecap="round"
3794
- strokeLinejoin="round"
3795
- style={{ flexShrink: 0, marginLeft: "4px" }}
3796
- aria-hidden="true"
3797
- >
3798
- <path d="M4 6l4 4 4-4" />
3799
- </svg>
3800
- </button>
3801
- </div>
3802
- </div>
3803
- {isLanguageDropdownOpen && (
3804
- <ul
3805
- id="language-listbox"
3806
- style={{
3807
- ...styles.languageDropdown,
3808
- ...languageSelectorStyle,
3809
- }}
3810
- role="listbox"
3811
- aria-label="Select language"
3812
- >
3813
- {availableLanguages.map((lang: string) => (
3814
- <li
3815
- key={lang}
3866
+ {logoNode}
3867
+ <div
3816
3868
  style={{
3817
- ...styles.languageItem,
3818
- ...responsiveStyles.languageItem,
3819
- ...(selectedLanguage === lang
3820
- ? selectedLanguageItemStyle
3821
- : languageSelectorStyle),
3822
- listStyle: "none",
3869
+ marginLeft: "auto",
3870
+ display: "flex",
3871
+ alignItems: "center",
3872
+ flexShrink: 0,
3823
3873
  }}
3824
- onClick={() => handleLanguageSelect(lang)}
3825
- role="option"
3826
- aria-selected={selectedLanguage === lang}
3827
- tabIndex={0}
3828
- aria-label={`Select ${toNativeLabel(lang)} language`}
3829
3874
  >
3830
- {toNativeLabel(lang)}
3831
- </li>
3832
- ))}
3833
- </ul>
3834
- )}
3835
- </div>
3875
+ {controlsBlock}
3876
+ </div>
3877
+ </div>
3878
+ {titleNode}
3879
+ </>
3880
+ ) : (
3881
+ <>
3882
+ <div style={styles.topLeft}>
3883
+ {logoNode}
3884
+ {titleNode}
3885
+ </div>
3886
+ {controlsBlock}
3887
+ </>
3888
+ );
3889
+ })()}
3836
3890
  </div>
3837
3891
  <div
3838
3892
  id="privacy-notice-description"
@@ -52,7 +52,8 @@ export const styles = {
52
52
  topSection: {
53
53
  display: "flex",
54
54
  justifyContent: "space-between",
55
- alignItems: "center",
55
+ alignItems: "flex-start",
56
+ gap: "10px",
56
57
  paddingBottom: "15px",
57
58
  borderBottom: "none",
58
59
  backgroundColor: "#ffffff",
@@ -65,6 +66,8 @@ export const styles = {
65
66
  display: "flex",
66
67
  alignItems: "center",
67
68
  gap: "10px",
69
+ flex: "1 1 0",
70
+ minWidth: 0,
68
71
  margin: 0,
69
72
  padding: 0,
70
73
  boxSizing: "border-box",
@@ -73,7 +76,9 @@ export const styles = {
73
76
  logo: {
74
77
  height: "32px",
75
78
  width: "auto",
79
+ maxWidth: "140px",
76
80
  objectFit: "contain",
81
+ flexShrink: 0,
77
82
  margin: 0,
78
83
  padding: 0,
79
84
  display: "block",
@@ -86,6 +91,10 @@ export const styles = {
86
91
  letterSpacing: "0.2px",
87
92
  verticalAlign: "middle",
88
93
  color: "#101828",
94
+ flex: "1 1 auto",
95
+ minWidth: 0,
96
+ wordBreak: "normal",
97
+ overflowWrap: "anywhere",
89
98
  margin: 0,
90
99
  padding: 0,
91
100
  textAlign: "left",
@@ -1,6 +1,5 @@
1
1
  import { useEffect, useState, useRef, type ReactNode } from 'react';
2
2
  import { OrganizationProvider } from './context/OrganizationContext';
3
- import { ThemeProvider } from './context/theme';
4
3
  import type { OrganizationDetail } from './lib/types';
5
4
  import {
6
5
  checkAndRefreshAuth,
@@ -14,12 +13,11 @@ import PrivacyCenterLayoutComponent from './components/PrivacyCenterLayout';
14
13
 
15
14
  interface PrivacyCenterLayoutProps {
16
15
  children: ReactNode;
17
- defaultTheme?: 'light' | 'dark';
18
16
  slug: string;
19
17
  onBack?: 'back' | 'signout';
20
18
  }
21
19
 
22
- export function PrivacyCenterLayout({ children, defaultTheme, slug, onBack }: PrivacyCenterLayoutProps) {
20
+ export function PrivacyCenterLayout({ children, slug, onBack }: PrivacyCenterLayoutProps) {
23
21
  const { consentClient } = useBaseUrl();
24
22
  // Read accessToken from AuthContext so this effect re-runs after AuthProvider
25
23
  // syncs JWT data to sessionStorage (fixes race condition where child effects
@@ -162,12 +160,10 @@ export function PrivacyCenterLayout({ children, defaultTheme, slug, onBack }: Pr
162
160
  }
163
161
 
164
162
  return (
165
- <ThemeProvider defaultTheme={defaultTheme}>
166
- <OrganizationProvider organization={organization}>
167
- <PrivacyCenterLayoutComponent onBack={onBack}>
168
- {children}
169
- </PrivacyCenterLayoutComponent>
170
- </OrganizationProvider>
171
- </ThemeProvider>
163
+ <OrganizationProvider organization={organization}>
164
+ <PrivacyCenterLayoutComponent onBack={onBack}>
165
+ {children}
166
+ </PrivacyCenterLayoutComponent>
167
+ </OrganizationProvider>
172
168
  );
173
169
  }
@@ -7,6 +7,7 @@ import { AuthProvider } from './context/AuthContext';
7
7
  import { NavigationProvider } from './context/NavigationContext';
8
8
  import type { InternalPage } from './context/NavigationContext';
9
9
  import { PageChromeProvider } from './context/PageChromeContext';
10
+ import { BrandingProvider } from './context/BrandingContext';
10
11
  import { injectPCStyles } from './styles/injectStyles';
11
12
 
12
13
  // Inject Privacy Center CSS styles on module load
@@ -15,7 +16,6 @@ injectPCStyles();
15
16
  interface RedactoPrivacyCenterProps {
16
17
  baseUrl: string; // Consent server base URL
17
18
  slug: string;
18
- theme?: 'light' | 'dark';
19
19
  accessToken: string;
20
20
  refreshToken: string;
21
21
  onError: (error: Error) => boolean;
@@ -26,7 +26,6 @@ interface RedactoPrivacyCenterProps {
26
26
  export function RedactoPrivacyCenter({
27
27
  baseUrl,
28
28
  slug,
29
- theme = 'light',
30
29
  accessToken,
31
30
  refreshToken,
32
31
  onError,
@@ -35,21 +34,23 @@ export function RedactoPrivacyCenter({
35
34
  }: RedactoPrivacyCenterProps) {
36
35
  return (
37
36
  <I18nextProvider i18n={i18n}>
38
- <BaseUrlProvider baseUrl={baseUrl}>
39
- <AuthProvider
40
- accessToken={accessToken}
41
- refreshToken={refreshToken}
42
- onError={onError}
43
- >
44
- <NavigationProvider initialPage={initialPage}>
45
- <PageChromeProvider>
46
- <PrivacyCenterLayout defaultTheme={theme} slug={slug} onBack={onBack}>
47
- <PrivacyCenterHomePage />
48
- </PrivacyCenterLayout>
49
- </PageChromeProvider>
50
- </NavigationProvider>
51
- </AuthProvider>
52
- </BaseUrlProvider>
37
+ <BrandingProvider slug={slug}>
38
+ <BaseUrlProvider baseUrl={baseUrl}>
39
+ <AuthProvider
40
+ accessToken={accessToken}
41
+ refreshToken={refreshToken}
42
+ onError={onError}
43
+ >
44
+ <NavigationProvider initialPage={initialPage}>
45
+ <PageChromeProvider>
46
+ <PrivacyCenterLayout slug={slug} onBack={onBack}>
47
+ <PrivacyCenterHomePage />
48
+ </PrivacyCenterLayout>
49
+ </PageChromeProvider>
50
+ </NavigationProvider>
51
+ </AuthProvider>
52
+ </BaseUrlProvider>
53
+ </BrandingProvider>
53
54
  </I18nextProvider>
54
55
  );
55
56
  }
@@ -0,0 +1,9 @@
1
+ import { apiFetch } from './fetcher';
2
+ import type { OrgBrandingDetail } from '../lib/types';
3
+
4
+ const USER_SERVER_BASE_URL = 'https://api.redacto.tech/user/api/0';
5
+
6
+ export async function fetchOrgBranding(slug: string): Promise<OrgBrandingDetail> {
7
+ const url = `${USER_SERVER_BASE_URL}/public/org-branding/by-slug/${encodeURIComponent(slug)}`;
8
+ return apiFetch<OrgBrandingDetail>(url, { method: 'GET' });
9
+ }
@@ -447,12 +447,17 @@ export default function ConsentManager({ variant }: ConsentManagerProps) {
447
447
  );
448
448
  setConsentRows(filteredRows);
449
449
 
450
- // Extract unique products from all groups
451
- const combinedGroups = [
452
- ...(response.direct || []),
453
- ...(response.nominated || []),
454
- ];
455
- setProductOptions(buildProductOptionsFromGroups(combinedGroups));
450
+ // Only refresh the product filter options from an unfiltered fetch.
451
+ // When a product filter is active the API returns rows for that one
452
+ // product, so re-deriving options here would shrink the dropdown to
453
+ // just the selected product (forcing the user to refresh to recover).
454
+ if (!productFilter) {
455
+ const combinedGroups = [
456
+ ...(response.direct || []),
457
+ ...(response.nominated || []),
458
+ ];
459
+ setProductOptions(buildProductOptionsFromGroups(combinedGroups));
460
+ }
456
461
  } catch (error) {
457
462
  console.error(error);
458
463
  setConsentDetail(null);
@@ -1122,14 +1127,11 @@ export default function ConsentManager({ variant }: ConsentManagerProps) {
1122
1127
  </>
1123
1128
  )}
1124
1129
 
1125
- {/* Always show pagination, even during loading */}
1126
- <div className="pc-consent-pagination-wrap">
1127
- {viewMode === 'product' ? (
1130
+ {viewMode === 'product' && (
1131
+ <div className="pc-consent-pagination-wrap">
1128
1132
  <AdvancedTablePagination table={productTable} />
1129
- ) : (
1130
- <AdvancedTablePagination table={purposeTable} />
1131
- )}
1132
- </div>
1133
+ </div>
1134
+ )}
1133
1135
  </div>
1134
1136
 
1135
1137
  <PCModal
@@ -1153,7 +1155,7 @@ export default function ConsentManager({ variant }: ConsentManagerProps) {
1153
1155
  }
1154
1156
  footer={
1155
1157
  <div className="pc-modal-footer-actions">
1156
- <PCButton variant="outline" onClick={() => setExportModal(false)}>
1158
+ <PCButton variant="secondary" onClick={() => setExportModal(false)}>
1157
1159
  {t('cancel')}
1158
1160
  </PCButton>
1159
1161
  <PCButton variant="primary" onClick={handleExport}>
@@ -1483,11 +1483,11 @@ export default function Form({
1483
1483
  className="pc-flex-responsive pc-spacing-responsive"
1484
1484
  >
1485
1485
  {isPopup ? (
1486
- <PCButton variant="outline" onClick={onClose} style={{ flexShrink: 0 }}>
1486
+ <PCButton variant="secondary" onClick={onClose} style={{ flexShrink: 0 }}>
1487
1487
  {t('cancel') ?? 'Cancel'}
1488
1488
  </PCButton>
1489
1489
  ) : (
1490
- <PCButton variant="outline" style={{ flexShrink: 0 }}>
1490
+ <PCButton variant="secondary" style={{ flexShrink: 0 }}>
1491
1491
  {t('saveDraft')}
1492
1492
  </PCButton>
1493
1493
  )}
@@ -1523,7 +1523,7 @@ export default function Form({
1523
1523
  footer={
1524
1524
  <div className="pc-erasure-modal-footer">
1525
1525
  <PCButton
1526
- variant="outline"
1526
+ variant="secondary"
1527
1527
  onClick={() => handleErasureModalChoice(false)}
1528
1528
  disabled={isLoading}
1529
1529
  >
@@ -300,7 +300,7 @@ export const ModifyConsentModal = ({
300
300
  ...pcStyles.gapSm,
301
301
  ...pcStyles.justifyEnd,
302
302
  }}>
303
- <PCButton variant="outline" onClick={onClose}>
303
+ <PCButton variant="secondary" onClick={onClose}>
304
304
  {t('cancel')}
305
305
  </PCButton>
306
306
  <PCButton