@redacto.io/consent-sdk-react 5.3.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +24 -0
- package/CHANGELOG.md +23 -0
- package/dist/privacy-center.d.mts +100 -42
- package/dist/privacy-center.d.ts +100 -42
- package/dist/privacy-center.js +4232 -2247
- package/dist/privacy-center.mjs +4266 -2284
- package/package.json +1 -1
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +6 -10
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +18 -17
- package/src/RedactoPrivacyCenter/api/actions.ts +52 -8
- package/src/RedactoPrivacyCenter/api/client.ts +58 -15
- package/src/RedactoPrivacyCenter/api/orgBranding.ts +9 -0
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +729 -278
- package/src/RedactoPrivacyCenter/components/ConsentManagerCells.tsx +187 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +65 -165
- package/src/RedactoPrivacyCenter/components/ConsentManagerExpandedRow.tsx +179 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerGroupedColumns.tsx +318 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerProductCards.tsx +333 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerPurposeCards.tsx +102 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +6 -2
- package/src/RedactoPrivacyCenter/components/Form.tsx +3 -3
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +9 -6
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx +1 -1
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +101 -56
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +16 -2
- package/src/RedactoPrivacyCenter/components/types.ts +2 -5
- package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +44 -0
- package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +21 -1
- package/src/RedactoPrivacyCenter/lib/api.ts +70 -28
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +4 -2
- package/src/RedactoPrivacyCenter/lib/types.ts +79 -36
- package/src/RedactoPrivacyCenter/locales/as/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/en/translation.json +16 -1
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +15 -1
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/or/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/te/translation.json +12 -1
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +12 -1
- package/src/RedactoPrivacyCenter/styles/applyBrandingTheme.ts +66 -0
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +528 -114
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +3 -2
- package/src/RedactoPrivacyCenter/styles/variables.css +5 -42
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +36 -16
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +4 -12
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +4 -10
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +1 -1
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +51 -5
- package/src/privacy-center.ts +7 -3
- package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +0 -85
- package/src/RedactoPrivacyCenter/context/theme.tsx +0 -127
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +0 -72
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { PCEmpty, PCText } from '../ui';
|
|
3
|
+
import type { ConsentModalRecord, DisplayPurposeRow } from '../lib/types';
|
|
4
|
+
import {
|
|
5
|
+
renderGivenDate,
|
|
6
|
+
renderMethod,
|
|
7
|
+
renderModifyButton,
|
|
8
|
+
renderStatusBadge,
|
|
9
|
+
renderValidTill,
|
|
10
|
+
} from './ConsentManagerCells';
|
|
11
|
+
|
|
12
|
+
type ConsentManagerPurposeCardsProps = {
|
|
13
|
+
rows: DisplayPurposeRow[];
|
|
14
|
+
onModify: (record: ConsentModalRecord) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const toModalRecord = (row: DisplayPurposeRow): ConsentModalRecord => ({
|
|
18
|
+
...row,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export function ConsentManagerPurposeCards({
|
|
22
|
+
rows,
|
|
23
|
+
onModify,
|
|
24
|
+
}: ConsentManagerPurposeCardsProps) {
|
|
25
|
+
const { t } = useTranslation();
|
|
26
|
+
|
|
27
|
+
if (rows.length === 0) {
|
|
28
|
+
return (
|
|
29
|
+
<div className="pc-consent-mobile-empty">
|
|
30
|
+
<PCEmpty title={t('noData')} />
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="pc-consent-mobile-list">
|
|
37
|
+
{rows.map((row) => (
|
|
38
|
+
<article
|
|
39
|
+
key={`${row.product_uuid}:${row.purpose_uuid}`}
|
|
40
|
+
className="pc-consent-mobile-card"
|
|
41
|
+
>
|
|
42
|
+
<div className="pc-consent-mobile-purpose-head">
|
|
43
|
+
<div style={{ minWidth: 0 }}>
|
|
44
|
+
<PCText size="sm" weight="semibold">{row.name}</PCText>
|
|
45
|
+
{row.description && (
|
|
46
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-subtle-text">
|
|
47
|
+
{row.description}
|
|
48
|
+
</PCText>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
{renderStatusBadge(row.status, t)}
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div className="pc-consent-mobile-activity-row">
|
|
55
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
56
|
+
{t('product')}
|
|
57
|
+
</PCText>
|
|
58
|
+
<PCText size="sm" weight="medium">{row.product_name || '-'}</PCText>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{row.nominator && (
|
|
62
|
+
<div className="pc-consent-mobile-activity-row">
|
|
63
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
64
|
+
{t('nominatedBy')}
|
|
65
|
+
</PCText>
|
|
66
|
+
<PCText size="sm" weight="medium">
|
|
67
|
+
{row.nominator.name || row.nominator.email || row.nominator.org_user_id}
|
|
68
|
+
</PCText>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
<div className="pc-consent-mobile-meta-grid">
|
|
73
|
+
<div>
|
|
74
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
75
|
+
{t('givenDate')}
|
|
76
|
+
</PCText>
|
|
77
|
+
{renderGivenDate(row.given_date, { showTooltip: false })}
|
|
78
|
+
</div>
|
|
79
|
+
<div>
|
|
80
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
81
|
+
{t('validTill')}
|
|
82
|
+
</PCText>
|
|
83
|
+
{renderValidTill(row, t)}
|
|
84
|
+
</div>
|
|
85
|
+
<div>
|
|
86
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
87
|
+
{t('method')}
|
|
88
|
+
</PCText>
|
|
89
|
+
{renderMethod(row.method)}
|
|
90
|
+
</div>
|
|
91
|
+
<div className="pc-consent-mobile-action-cell">
|
|
92
|
+
<PCText size="sm" color="secondary" className="pc-consent-mobile-meta-label">
|
|
93
|
+
{t('action')}
|
|
94
|
+
</PCText>
|
|
95
|
+
{renderModifyButton(toModalRecord(row), onModify, t)}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</article>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -6,8 +6,9 @@ const getVar = (varName: string): string => `var(${varName})`;
|
|
|
6
6
|
export const ConsentManagerTableSkeleton = () => {
|
|
7
7
|
return (
|
|
8
8
|
<div style={{
|
|
9
|
-
borderRadius: getVar('--pc-border-radius-
|
|
9
|
+
borderRadius: `${getVar('--pc-border-radius-lg')} ${getVar('--pc-border-radius-lg')} 0 0`,
|
|
10
10
|
border: `1px solid ${getVar('--pc-border-color')}`,
|
|
11
|
+
borderBottom: 'none',
|
|
11
12
|
backgroundColor: getVar('--pc-bg-primary'),
|
|
12
13
|
}}>
|
|
13
14
|
{/* Table Header */}
|
|
@@ -44,7 +45,10 @@ export const ConsentManagerTableSkeleton = () => {
|
|
|
44
45
|
<div
|
|
45
46
|
key={index}
|
|
46
47
|
style={{
|
|
47
|
-
borderBottom:
|
|
48
|
+
borderBottom:
|
|
49
|
+
index === SKELETON_ROW_COUNT - 1
|
|
50
|
+
? 'none'
|
|
51
|
+
: `1px solid ${getVar('--pc-border-color')}`,
|
|
48
52
|
backgroundColor: getVar('--pc-bg-primary'),
|
|
49
53
|
}}
|
|
50
54
|
>
|
|
@@ -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="
|
|
1486
|
+
<PCButton variant="secondary" onClick={onClose} style={{ flexShrink: 0 }}>
|
|
1487
1487
|
{t('cancel') ?? 'Cancel'}
|
|
1488
1488
|
</PCButton>
|
|
1489
1489
|
) : (
|
|
1490
|
-
<PCButton variant="
|
|
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="
|
|
1526
|
+
variant="secondary"
|
|
1527
1527
|
onClick={() => handleErasureModalChoice(false)}
|
|
1528
1528
|
disabled={isLoading}
|
|
1529
1529
|
>
|
|
@@ -19,7 +19,7 @@ export const ModifyConsentModal = ({
|
|
|
19
19
|
const [isAccordionOpen, setIsAccordionOpen] = useState(false);
|
|
20
20
|
const [selectedDataElementUuids, setSelectedDataElementUuids] = useState<string[]>([]);
|
|
21
21
|
|
|
22
|
-
const nominatorInfo = selectedRecord?.
|
|
22
|
+
const nominatorInfo = selectedRecord?.nominator;
|
|
23
23
|
const nominatorLabel = nominatorInfo?.name || nominatorInfo?.email || nominatorInfo?.org_user_id || null;
|
|
24
24
|
|
|
25
25
|
const actionConfig = useMemo(() => {
|
|
@@ -39,7 +39,10 @@ export const ModifyConsentModal = ({
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
if (
|
|
42
|
+
if (
|
|
43
|
+
status === ConsentStatusEnum.WITHDRAWN ||
|
|
44
|
+
status === ConsentStatusEnum.REVOKED
|
|
45
|
+
) {
|
|
43
46
|
return {
|
|
44
47
|
action: 'regrant' as ConsentAction,
|
|
45
48
|
buttonLabel: t('regrantConsent'),
|
|
@@ -158,7 +161,7 @@ export const ModifyConsentModal = ({
|
|
|
158
161
|
fontWeight: pcStyles.fontWeightSemibold.fontWeight,
|
|
159
162
|
color: pcStyles.textPrimary.color,
|
|
160
163
|
}}>
|
|
161
|
-
{selectedRecord.
|
|
164
|
+
{selectedRecord.name}
|
|
162
165
|
</p>
|
|
163
166
|
<p style={{
|
|
164
167
|
textAlign: 'left',
|
|
@@ -167,7 +170,7 @@ export const ModifyConsentModal = ({
|
|
|
167
170
|
color: pcStyles.textSecondary.color,
|
|
168
171
|
margin: 0,
|
|
169
172
|
}}>
|
|
170
|
-
{selectedRecord.
|
|
173
|
+
{selectedRecord.description}
|
|
171
174
|
</p>
|
|
172
175
|
</div>
|
|
173
176
|
<ChevronDownIcon
|
|
@@ -297,7 +300,7 @@ export const ModifyConsentModal = ({
|
|
|
297
300
|
...pcStyles.gapSm,
|
|
298
301
|
...pcStyles.justifyEnd,
|
|
299
302
|
}}>
|
|
300
|
-
<PCButton variant="
|
|
303
|
+
<PCButton variant="secondary" onClick={onClose}>
|
|
301
304
|
{t('cancel')}
|
|
302
305
|
</PCButton>
|
|
303
306
|
<PCButton
|
|
@@ -309,7 +312,7 @@ export const ModifyConsentModal = ({
|
|
|
309
312
|
variant={actionConfig.buttonVariant}
|
|
310
313
|
onClick={() => {
|
|
311
314
|
if (selectedRecord.purpose_uuid) {
|
|
312
|
-
const nomContact = selectedRecord.
|
|
315
|
+
const nomContact = selectedRecord.nominator?.email || selectedRecord.nominator?.org_user_id || undefined;
|
|
313
316
|
onManageConsent(
|
|
314
317
|
selectedRecord.purpose_uuid,
|
|
315
318
|
actionConfig.action,
|
|
@@ -148,7 +148,7 @@ export const NotificationBell = () => {
|
|
|
148
148
|
<PCTooltip content={t('notifications')} placement="bottom" color="invert">
|
|
149
149
|
<div className="pc-notification-bell-wrapper">
|
|
150
150
|
<button type="button" aria-label={t('notifications')} className="pc-notification-bell-btn">
|
|
151
|
-
<IoNotificationsOutline size={
|
|
151
|
+
<IoNotificationsOutline size={18} />
|
|
152
152
|
</button>
|
|
153
153
|
{unreadCount > 0 && (
|
|
154
154
|
<span className="pc-notification-badge">{unreadCount}</span>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Suspense, useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import SideBarPC from './SideMenuBarPC/SideBarPC';
|
|
3
3
|
import Footer from '../ui/Footer';
|
|
4
|
-
import ThemeToggle from '../ui/ThemeToggle';
|
|
5
|
-
import ThemeTransitionOverlay from './ThemeTransitionOverlay';
|
|
6
4
|
import { PCSelect } from '../ui';
|
|
7
5
|
import { useTranslation } from 'react-i18next';
|
|
8
6
|
import { PAGE_CHROME_BY_PAGE, languageOptions } from '../lib/constants';
|
|
@@ -14,6 +12,7 @@ import { pcStyles } from '../styles/pcStyles';
|
|
|
14
12
|
import type { PrivacyCenterLayoutProps } from './types';
|
|
15
13
|
import { NotificationBell } from './NotificationHub/NotificationBell';
|
|
16
14
|
import { getVar } from '../lib/utils';
|
|
15
|
+
import { Globe2 } from 'lucide-react';
|
|
17
16
|
|
|
18
17
|
export default function PrivacyCenterLayout({
|
|
19
18
|
children,
|
|
@@ -23,7 +22,7 @@ export default function PrivacyCenterLayout({
|
|
|
23
22
|
}: PrivacyCenterLayoutProps) {
|
|
24
23
|
const { t } = useTranslation();
|
|
25
24
|
const { currentPage } = useInternalNavigation();
|
|
26
|
-
const { pageChromeOverride } = usePageChrome();
|
|
25
|
+
const { pageChromeOverride, headerStatus } = usePageChrome();
|
|
27
26
|
const [locale, setLocale] = useState(i18n.language);
|
|
28
27
|
const pageChrome = pageChromeOverride ?? PAGE_CHROME_BY_PAGE[currentPage];
|
|
29
28
|
|
|
@@ -41,7 +40,6 @@ export default function PrivacyCenterLayout({
|
|
|
41
40
|
return (
|
|
42
41
|
<LocaleContext.Provider value={{ locale, setLocale }}>
|
|
43
42
|
<Suspense fallback={<div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter, ...pcStyles.justifyCenter, minHeight: '100vh' }}>{t('loadingEllipsis')}</div>}>
|
|
44
|
-
<ThemeTransitionOverlay />
|
|
45
43
|
<main
|
|
46
44
|
className="pc-root"
|
|
47
45
|
style={{
|
|
@@ -59,12 +57,11 @@ export default function PrivacyCenterLayout({
|
|
|
59
57
|
flex: 1,
|
|
60
58
|
minWidth: 0,
|
|
61
59
|
backgroundColor: getVar('--pc-bg-shell'),
|
|
60
|
+
minHeight: 0,
|
|
62
61
|
}} className="pc-content-wrapper">
|
|
63
62
|
{/* Top Navbar with Theme Toggle */}
|
|
64
63
|
<nav style={{
|
|
65
|
-
position: '
|
|
66
|
-
top: 0,
|
|
67
|
-
zIndex: 50,
|
|
64
|
+
position: 'relative',
|
|
68
65
|
...pcStyles.flex,
|
|
69
66
|
...pcStyles.itemsCenter,
|
|
70
67
|
...pcStyles.justifyBetween,
|
|
@@ -74,8 +71,8 @@ export default function PrivacyCenterLayout({
|
|
|
74
71
|
paddingRight: 'var(--pc-navbar-padding-right, 0.75rem)',
|
|
75
72
|
paddingTop: 'var(--pc-navbar-padding-top, 0.625rem)',
|
|
76
73
|
paddingBottom: 'var(--pc-navbar-padding-bottom, 0.625rem)',
|
|
77
|
-
transition: 'background-color 0.3s ease, border-color 0.3s ease
|
|
78
|
-
willChange: 'background-color, border-color
|
|
74
|
+
transition: 'background-color 0.3s ease, border-color 0.3s ease',
|
|
75
|
+
willChange: 'background-color, border-color',
|
|
79
76
|
}} className="pc-navbar">
|
|
80
77
|
<div
|
|
81
78
|
className="pc-navbar-page-meta"
|
|
@@ -85,32 +82,76 @@ export default function PrivacyCenterLayout({
|
|
|
85
82
|
justifyContent: 'center',
|
|
86
83
|
minWidth: 0,
|
|
87
84
|
flex: 1,
|
|
88
|
-
gap: '0.
|
|
85
|
+
gap: '0.1875rem',
|
|
89
86
|
}}
|
|
90
87
|
>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem', minWidth: 0 }}>
|
|
89
|
+
<p
|
|
90
|
+
className="pc-navbar-title"
|
|
91
|
+
style={{
|
|
92
|
+
margin: 0,
|
|
93
|
+
color: getVar('--pc-text-primary'),
|
|
94
|
+
fontSize: '1.0625rem',
|
|
95
|
+
fontWeight: 700,
|
|
96
|
+
lineHeight: 1.2,
|
|
97
|
+
whiteSpace: 'nowrap',
|
|
98
|
+
overflow: 'hidden',
|
|
99
|
+
textOverflow: 'ellipsis',
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{t(pageChrome.titleKey)}
|
|
103
|
+
</p>
|
|
104
|
+
{headerStatus && (
|
|
105
|
+
<span
|
|
106
|
+
style={{
|
|
107
|
+
display: 'inline-flex',
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
flexShrink: 0,
|
|
110
|
+
borderRadius: '9999px',
|
|
111
|
+
gap: '0.375rem',
|
|
112
|
+
padding: '0.25rem 0.75rem',
|
|
113
|
+
fontSize: '0.625rem',
|
|
114
|
+
fontWeight: 600,
|
|
115
|
+
lineHeight: 1.2,
|
|
116
|
+
border: `1px solid ${
|
|
117
|
+
headerStatus.variant === 'success'
|
|
118
|
+
? 'rgba(16, 185, 129, 0.35)'
|
|
119
|
+
: 'rgba(245, 158, 11, 0.35)'
|
|
120
|
+
}`,
|
|
121
|
+
backgroundColor:
|
|
122
|
+
headerStatus.variant === 'success'
|
|
123
|
+
? 'rgba(16, 185, 129, 0.1)'
|
|
124
|
+
: '#fff7ed',
|
|
125
|
+
color:
|
|
126
|
+
headerStatus.variant === 'success'
|
|
127
|
+
? getVar('--pc-success')
|
|
128
|
+
: '#d97706',
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<span
|
|
132
|
+
aria-hidden="true"
|
|
133
|
+
style={{
|
|
134
|
+
width: '0.4375rem',
|
|
135
|
+
height: '0.4375rem',
|
|
136
|
+
borderRadius: '9999px',
|
|
137
|
+
backgroundColor:
|
|
138
|
+
headerStatus.variant === 'success'
|
|
139
|
+
? getVar('--pc-success')
|
|
140
|
+
: '#d97706',
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
{headerStatus.label}
|
|
144
|
+
</span>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
106
147
|
{pageChrome.descriptionKey && (
|
|
107
148
|
<p
|
|
108
149
|
className="pc-navbar-description"
|
|
109
150
|
style={{
|
|
110
151
|
margin: 0,
|
|
111
152
|
color: getVar('--pc-text-secondary'),
|
|
112
|
-
fontSize: '0.
|
|
113
|
-
lineHeight: 1.
|
|
153
|
+
fontSize: '0.75rem',
|
|
154
|
+
lineHeight: 1.2,
|
|
114
155
|
whiteSpace: 'nowrap',
|
|
115
156
|
overflow: 'hidden',
|
|
116
157
|
textOverflow: 'ellipsis',
|
|
@@ -123,18 +164,20 @@ export default function PrivacyCenterLayout({
|
|
|
123
164
|
<div style={{
|
|
124
165
|
display: 'flex',
|
|
125
166
|
alignItems: 'center',
|
|
126
|
-
gap: '0.
|
|
167
|
+
gap: '0.375rem',
|
|
127
168
|
flexShrink: 0,
|
|
128
169
|
}} className="pc-navbar-actions">
|
|
129
170
|
<PCSelect
|
|
171
|
+
className="pc-navbar-language-select"
|
|
130
172
|
size="sm"
|
|
131
173
|
searchable
|
|
174
|
+
prefix={<Globe2 size={12} strokeWidth={1.8} />}
|
|
132
175
|
searchPlaceholder={t('searchPlaceholder')}
|
|
133
176
|
noOptionsMessage={t('noOptionsFound')}
|
|
134
177
|
style={{
|
|
135
178
|
width: 'auto',
|
|
136
|
-
minWidth: '
|
|
137
|
-
maxWidth: '
|
|
179
|
+
minWidth: '120px',
|
|
180
|
+
maxWidth: 'min(65vw, 320px)',
|
|
138
181
|
flexShrink: 0,
|
|
139
182
|
}}
|
|
140
183
|
options={languageOptions}
|
|
@@ -142,34 +185,35 @@ export default function PrivacyCenterLayout({
|
|
|
142
185
|
onChange={handleLanguageChange}
|
|
143
186
|
/>
|
|
144
187
|
<NotificationBell />
|
|
145
|
-
<ThemeToggle />
|
|
146
188
|
</div>
|
|
147
189
|
</nav>
|
|
148
190
|
<div style={{
|
|
149
191
|
...pcStyles.flex,
|
|
150
192
|
...pcStyles.flexCol,
|
|
151
|
-
|
|
193
|
+
flex: 1,
|
|
194
|
+
minHeight: 0,
|
|
152
195
|
paddingLeft: 'var(--pc-content-padding-left, 0.75rem)',
|
|
153
196
|
paddingRight: 'var(--pc-content-padding-right, 0.75rem)',
|
|
154
|
-
paddingTop: 'var(--pc-content-padding-top,
|
|
197
|
+
paddingTop: 'var(--pc-content-padding-top, 0)',
|
|
155
198
|
minWidth: 0,
|
|
156
199
|
backgroundColor: getVar('--pc-bg-shell'),
|
|
157
200
|
}} className="pc-content-container">
|
|
158
201
|
<div style={{
|
|
159
|
-
|
|
202
|
+
display: 'flex',
|
|
203
|
+
flexDirection: 'column',
|
|
204
|
+
flex: 1,
|
|
205
|
+
minHeight: 0,
|
|
160
206
|
width: '100%',
|
|
161
207
|
}}>
|
|
162
208
|
<div style={{
|
|
163
209
|
...pcStyles.flex,
|
|
164
210
|
...pcStyles.flexCol,
|
|
165
|
-
|
|
166
|
-
minHeight:
|
|
211
|
+
flex: 1,
|
|
212
|
+
minHeight: 0,
|
|
167
213
|
...pcStyles.justifyBetween,
|
|
168
|
-
...pcStyles.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
paddingTop: '1rem',
|
|
172
|
-
paddingBottom: '1rem',
|
|
214
|
+
...pcStyles.gapSm,
|
|
215
|
+
paddingTop: '0.625rem',
|
|
216
|
+
paddingBottom: '0.75rem',
|
|
173
217
|
}}>
|
|
174
218
|
<div className={contentClassName} style={{ width: '100%' }}>{children}</div>
|
|
175
219
|
<div style={{
|
|
@@ -199,17 +243,14 @@ export default function PrivacyCenterLayout({
|
|
|
199
243
|
.pc-navbar {
|
|
200
244
|
--pc-navbar-padding-left: 0.75rem;
|
|
201
245
|
--pc-navbar-padding-right: 0.75rem;
|
|
202
|
-
--pc-navbar-padding-top: 0.
|
|
203
|
-
--pc-navbar-padding-bottom: 0.
|
|
204
|
-
left: var(--pc-sidebar-offset) !important;
|
|
205
|
-
width: calc(100% - var(--pc-sidebar-offset)) !important;
|
|
206
|
-
transition: left 0.3s ease, width 0.3s ease !important;
|
|
246
|
+
--pc-navbar-padding-top: 0.875rem;
|
|
247
|
+
--pc-navbar-padding-bottom: 0.75rem;
|
|
207
248
|
}
|
|
208
249
|
|
|
209
250
|
.pc-content-container {
|
|
210
251
|
--pc-content-padding-left: 0.75rem;
|
|
211
252
|
--pc-content-padding-right: 0.75rem;
|
|
212
|
-
--pc-content-padding-top:
|
|
253
|
+
--pc-content-padding-top: 0;
|
|
213
254
|
}
|
|
214
255
|
|
|
215
256
|
.pc-navbar-page-meta {
|
|
@@ -247,7 +288,7 @@ export default function PrivacyCenterLayout({
|
|
|
247
288
|
.pc-navbar {
|
|
248
289
|
--pc-navbar-padding-left: 3.75rem;
|
|
249
290
|
--pc-navbar-padding-right: 0.75rem;
|
|
250
|
-
--pc-navbar-padding-top: 0.
|
|
291
|
+
--pc-navbar-padding-top: 0.75rem;
|
|
251
292
|
--pc-navbar-padding-bottom: 0.625rem;
|
|
252
293
|
}
|
|
253
294
|
.pc-navbar-description {
|
|
@@ -261,21 +302,21 @@ export default function PrivacyCenterLayout({
|
|
|
261
302
|
gap: 0.375rem;
|
|
262
303
|
}
|
|
263
304
|
.pc-content-container {
|
|
264
|
-
--pc-content-padding-top:
|
|
305
|
+
--pc-content-padding-top: 0;
|
|
265
306
|
}
|
|
266
307
|
}
|
|
267
308
|
@media (min-width: 768px) {
|
|
268
309
|
.pc-navbar {
|
|
269
|
-
--pc-navbar-padding-left:
|
|
270
|
-
--pc-navbar-padding-right:
|
|
271
|
-
--pc-navbar-padding-top: 0.
|
|
310
|
+
--pc-navbar-padding-left: 1.25rem;
|
|
311
|
+
--pc-navbar-padding-right: 1.25rem;
|
|
312
|
+
--pc-navbar-padding-top: 0.875rem;
|
|
272
313
|
--pc-navbar-padding-bottom: 0.75rem;
|
|
273
314
|
}
|
|
274
315
|
.pc-navbar-description {
|
|
275
316
|
display: block;
|
|
276
317
|
}
|
|
277
318
|
.pc-content-container {
|
|
278
|
-
--pc-content-padding-top:
|
|
319
|
+
--pc-content-padding-top: 0;
|
|
279
320
|
}
|
|
280
321
|
}
|
|
281
322
|
@media (min-width: 768px) {
|
|
@@ -283,19 +324,23 @@ export default function PrivacyCenterLayout({
|
|
|
283
324
|
font-size: 1.0625rem;
|
|
284
325
|
}
|
|
285
326
|
.pc-navbar-description {
|
|
286
|
-
font-size: 0.
|
|
327
|
+
font-size: 0.6875rem;
|
|
287
328
|
}
|
|
288
329
|
.pc-content-container {
|
|
289
330
|
--pc-content-padding-left: 1.25rem;
|
|
290
331
|
--pc-content-padding-right: 1.25rem;
|
|
291
|
-
--pc-content-padding-top:
|
|
332
|
+
--pc-content-padding-top: 0;
|
|
292
333
|
}
|
|
293
334
|
}
|
|
294
335
|
@media (min-width: 1024px) {
|
|
336
|
+
.pc-navbar {
|
|
337
|
+
--pc-navbar-padding-left: 1.5rem;
|
|
338
|
+
--pc-navbar-padding-right: 1.5rem;
|
|
339
|
+
}
|
|
295
340
|
.pc-content-container {
|
|
296
341
|
--pc-content-padding-left: 1.5rem;
|
|
297
342
|
--pc-content-padding-right: 1.5rem;
|
|
298
|
-
--pc-content-padding-top:
|
|
343
|
+
--pc-content-padding-top: 0;
|
|
299
344
|
}
|
|
300
345
|
}
|
|
301
346
|
`}</style>
|
|
@@ -18,6 +18,7 @@ const getVar = (varName: string): string => `var(${varName})`;
|
|
|
18
18
|
import { SideMenuPC } from './SideMenuPC';
|
|
19
19
|
import { useMenuItemsPC } from '../../lib/constants';
|
|
20
20
|
import { useOrganization } from '../../context/OrganizationContext';
|
|
21
|
+
import { useBranding } from '../../context/BrandingContext';
|
|
21
22
|
import type { SideBarPCProps } from './types';
|
|
22
23
|
import { signOutPrivacyCenter } from '../../lib/utils';
|
|
23
24
|
|
|
@@ -34,6 +35,7 @@ export default function SideBarPC({
|
|
|
34
35
|
const [email, setEmail] = useState<string>('');
|
|
35
36
|
// const [isSigningOut, setIsSigningOut] = useState(false);
|
|
36
37
|
const { organization } = useOrganization();
|
|
38
|
+
const branding = useBranding();
|
|
37
39
|
|
|
38
40
|
useEffect(() => {
|
|
39
41
|
setEmail(sessionStorage.getItem('email') || '');
|
|
@@ -109,6 +111,8 @@ export default function SideBarPC({
|
|
|
109
111
|
}, [isMobileOpen]);
|
|
110
112
|
|
|
111
113
|
const organizationName = organization?.name || '';
|
|
114
|
+
const displayName = branding?.brand_name || organizationName;
|
|
115
|
+
const isExpanded = isMobileOpen || !isCollapsed;
|
|
112
116
|
|
|
113
117
|
const sidebarClassName = useMemo(
|
|
114
118
|
() =>
|
|
@@ -325,7 +329,17 @@ export default function SideBarPC({
|
|
|
325
329
|
transition: 'transform 0.3s ease',
|
|
326
330
|
willChange: 'transform',
|
|
327
331
|
}}>
|
|
328
|
-
|
|
332
|
+
{isExpanded && branding?.logo_url ? (
|
|
333
|
+
<img
|
|
334
|
+
src={branding.logo_url}
|
|
335
|
+
alt={displayName}
|
|
336
|
+
style={{ height: '32px', width: 'auto', maxWidth: '160px', objectFit: 'contain', display: 'block' }}
|
|
337
|
+
/>
|
|
338
|
+
) : branding?.icon_url ? (
|
|
339
|
+
<PCAvatar src={branding.icon_url} name={displayName} color="info" size="md" />
|
|
340
|
+
) : (
|
|
341
|
+
<PCAvatar name={displayName} color="info" size="md" />
|
|
342
|
+
)}
|
|
329
343
|
</div>
|
|
330
344
|
<div style={headerTextStyle}>
|
|
331
345
|
<PCText style={{
|
|
@@ -337,7 +351,7 @@ export default function SideBarPC({
|
|
|
337
351
|
transition: 'opacity 0.3s ease, transform 0.3s ease',
|
|
338
352
|
willChange: 'opacity, transform',
|
|
339
353
|
}}>
|
|
340
|
-
{
|
|
354
|
+
{displayName}
|
|
341
355
|
</PCText>
|
|
342
356
|
</div>
|
|
343
357
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { CaseRequest,
|
|
2
|
+
import type { CaseRequest, ConsentModalRecord } from '../lib/types';
|
|
3
3
|
|
|
4
4
|
export type TableColumnAlign = 'left' | 'center' | 'right';
|
|
5
5
|
|
|
@@ -61,7 +61,7 @@ export type ConsentManagerProps = {
|
|
|
61
61
|
export type ModifyConsentModalProps = {
|
|
62
62
|
isOpen: boolean;
|
|
63
63
|
onClose: () => void;
|
|
64
|
-
selectedRecord:
|
|
64
|
+
selectedRecord: ConsentModalRecord | null;
|
|
65
65
|
isLoading: boolean;
|
|
66
66
|
onManageConsent: (
|
|
67
67
|
purposeId: string,
|
|
@@ -71,6 +71,3 @@ export type ModifyConsentModalProps = {
|
|
|
71
71
|
) => void;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
export type UseConsentManagerColumnsProps = {
|
|
75
|
-
onOpenModal: (record: UserConsent) => void;
|
|
76
|
-
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
|
|
2
|
+
import { fetchOrgBranding } from '../api/orgBranding';
|
|
3
|
+
import { applyBrandingTheme, clearBrandingTheme } from '../styles/applyBrandingTheme';
|
|
4
|
+
import type { OrgBrandingDetail } from '../lib/types';
|
|
5
|
+
|
|
6
|
+
type BrandingContextValue = { branding: OrgBrandingDetail | null };
|
|
7
|
+
|
|
8
|
+
const BrandingContext = createContext<BrandingContextValue | null>(null);
|
|
9
|
+
|
|
10
|
+
export const BrandingProvider = ({ children, slug }: { children: ReactNode; slug: string }) => {
|
|
11
|
+
const [branding, setBranding] = useState<OrgBrandingDetail | null>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!slug) return;
|
|
15
|
+
let cancelled = false;
|
|
16
|
+
fetchOrgBranding(slug)
|
|
17
|
+
.then((data) => {
|
|
18
|
+
if (!cancelled) setBranding(data);
|
|
19
|
+
})
|
|
20
|
+
.catch((error) => {
|
|
21
|
+
console.warn('[RedactoPrivacyCenter] org branding fetch failed:', error);
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
cancelled = true;
|
|
25
|
+
};
|
|
26
|
+
}, [slug]);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!branding) return;
|
|
30
|
+
applyBrandingTheme(branding.theme);
|
|
31
|
+
return clearBrandingTheme;
|
|
32
|
+
}, [branding]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<BrandingContext.Provider value={{ branding }}>
|
|
36
|
+
{children}
|
|
37
|
+
</BrandingContext.Provider>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const useBranding = (): OrgBrandingDetail | null => {
|
|
42
|
+
const ctx = useContext(BrandingContext);
|
|
43
|
+
return ctx?.branding ?? null;
|
|
44
|
+
};
|