@redacto.io/consent-sdk-react 5.2.0 → 6.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 +12 -0
- package/dist/index.d.mts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +149 -206
- package/dist/index.mjs +148 -206
- package/dist/privacy-center.d.mts +178 -39
- package/dist/privacy-center.d.ts +178 -39
- package/dist/privacy-center.js +10534 -6795
- package/dist/privacy-center.mjs +7922 -4181
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +16 -20
- package/src/RedactoNoticeConsent/api/index.ts +21 -103
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +5 -4
- package/src/RedactoNoticeConsentInline/api/index.ts +64 -117
- package/src/RedactoPrivacyCenter/NotificationsPage.tsx +174 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +3 -0
- package/src/RedactoPrivacyCenter/api/actions.ts +136 -8
- package/src/RedactoPrivacyCenter/api/client.ts +176 -15
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +722 -273
- 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/DataRequest/PurposeSelectionSection.tsx +15 -0
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +8 -5
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationBell.tsx +160 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationCTAButton.tsx +18 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdown.tsx +54 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationDropdownItem.tsx +57 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationIcon.tsx +19 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationListPanel.tsx +92 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRefreshButton.tsx +20 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/NotificationRow.tsx +58 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/StatCard.tsx +10 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/useNotificationActions.ts +112 -0
- package/src/RedactoPrivacyCenter/components/NotificationHub/utils.ts +27 -0
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +105 -55
- package/src/RedactoPrivacyCenter/components/types.ts +2 -5
- package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +2 -2
- package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +21 -1
- package/src/RedactoPrivacyCenter/lib/api.ts +147 -28
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +4 -2
- package/src/RedactoPrivacyCenter/lib/constants.ts +78 -1
- package/src/RedactoPrivacyCenter/lib/types.ts +205 -36
- package/src/RedactoPrivacyCenter/lib/utils.ts +3 -0
- 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 +42 -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/injectStyles.ts +1027 -0
- package/src/RedactoPrivacyCenter/styles/variables.css +5 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +36 -16
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +4 -12
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +1 -1
- package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +23 -1
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +51 -5
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +2 -4
- package/src/index.ts +5 -0
- package/src/privacy-center.ts +8 -3
- package/src/shared/api-errors.ts +171 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import type { NotificationRowProps } from '../../lib/types';
|
|
3
|
+
import { NotificationIcon } from './NotificationIcon';
|
|
4
|
+
import { NotificationCTAButton } from './NotificationCTAButton';
|
|
5
|
+
import { formatTimeAgo } from './utils';
|
|
6
|
+
|
|
7
|
+
export const NotificationRow = ({ notification, onClick, onCTAClick }: NotificationRowProps) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const statusLabel = notification.status_label_display || notification.status_label;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
onClick={onClick}
|
|
14
|
+
onKeyDown={(e) => {
|
|
15
|
+
if (e.currentTarget !== e.target) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
onClick();
|
|
21
|
+
}
|
|
22
|
+
}}
|
|
23
|
+
role="button"
|
|
24
|
+
tabIndex={0}
|
|
25
|
+
className="pc-notification-row"
|
|
26
|
+
data-read={notification.is_read ? 'true' : 'false'}
|
|
27
|
+
>
|
|
28
|
+
<NotificationIcon notificationType={notification.notification_type} size="lg" />
|
|
29
|
+
|
|
30
|
+
<div className="pc-notification-row-content">
|
|
31
|
+
<div className="pc-notification-row-title-line">
|
|
32
|
+
<span className="pc-notification-row-title">{notification.title}</span>
|
|
33
|
+
{!notification.is_read && <span className="pc-notification-unread-dot-lg" />}
|
|
34
|
+
{statusLabel && (
|
|
35
|
+
<span className="pc-notification-status-badge">{statusLabel}</span>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<p className="pc-notification-row-description">{notification.description}</p>
|
|
40
|
+
|
|
41
|
+
{notification.ctas && notification.ctas.length > 0 && (
|
|
42
|
+
<div className="pc-notification-row-ctas">
|
|
43
|
+
{notification.ctas.map((cta) => (
|
|
44
|
+
<NotificationCTAButton
|
|
45
|
+
key={`${cta.action_type}-${cta.label}`}
|
|
46
|
+
cta={cta}
|
|
47
|
+
size="lg"
|
|
48
|
+
onClick={(e) => { e.stopPropagation(); onCTAClick(cta); }}
|
|
49
|
+
/>
|
|
50
|
+
))}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<span className="pc-notification-row-time">{formatTimeAgo(notification.created_at, t)}</span>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StatCardProps } from '../../lib/types';
|
|
2
|
+
|
|
3
|
+
export const StatCard = ({ value, label }: StatCardProps) => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="pc-notification-stat-card">
|
|
6
|
+
<span className="pc-notification-stat-value">{value}</span>
|
|
7
|
+
<span className="pc-notification-stat-label">{label}</span>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { useInternalNavigation } from '../../context/NavigationContext';
|
|
3
|
+
import { useOrganization } from '../../context/OrganizationContext';
|
|
4
|
+
import { useAuth } from '../../context/AuthContext';
|
|
5
|
+
import { useBaseUrl } from '../../context/BaseUrlContext';
|
|
6
|
+
import { markNotificationRead, markAllRead, acknowledgeNotification, fetchCaseHistory } from '../../lib/api';
|
|
7
|
+
import type { AuthContext } from '../../lib/api';
|
|
8
|
+
import { useCurrentLanguage } from '../../lib/useCurrentLanguage';
|
|
9
|
+
import type { Notification, NotificationCTA } from '../../lib/types';
|
|
10
|
+
import { CASE_DETAILS_PREFIX } from '../../lib/constants';
|
|
11
|
+
|
|
12
|
+
export const useNotificationActions = () => {
|
|
13
|
+
const { navigateTo, setSelectedCase } = useInternalNavigation();
|
|
14
|
+
const { organization } = useOrganization();
|
|
15
|
+
const { accessToken, refreshToken, onError, updateTokens } = useAuth();
|
|
16
|
+
const { consentClient } = useBaseUrl();
|
|
17
|
+
const { langParam } = useCurrentLanguage();
|
|
18
|
+
|
|
19
|
+
const organizationId = organization?.uuid;
|
|
20
|
+
const workspaceId = organization?.workspaces[0]?.uuid;
|
|
21
|
+
|
|
22
|
+
const authContext: AuthContext = useMemo(() => ({
|
|
23
|
+
accessToken, refreshToken, onError, updateTokens,
|
|
24
|
+
}), [accessToken, refreshToken, onError, updateTokens]);
|
|
25
|
+
|
|
26
|
+
const hasAcknowledgeCTA = useCallback((notification: Notification) => {
|
|
27
|
+
return notification.ctas?.some((cta) => cta.action_type === 'acknowledge') ?? false;
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const handleMarkRead = useCallback(async (notificationUuid: string): Promise<boolean> => {
|
|
31
|
+
if (!organizationId || !workspaceId) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const result = await markNotificationRead(organizationId, workspaceId, notificationUuid, authContext, consentClient);
|
|
35
|
+
return result !== null;
|
|
36
|
+
}, [organizationId, workspaceId, authContext, consentClient]);
|
|
37
|
+
|
|
38
|
+
const handleMarkAllRead = useCallback(async (): Promise<boolean> => {
|
|
39
|
+
if (!organizationId || !workspaceId) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const result = await markAllRead(organizationId, workspaceId, authContext, consentClient);
|
|
43
|
+
return result !== null;
|
|
44
|
+
}, [organizationId, workspaceId, authContext, consentClient]);
|
|
45
|
+
|
|
46
|
+
const handleAcknowledge = useCallback(async (notificationUuid: string): Promise<boolean> => {
|
|
47
|
+
if (!organizationId || !workspaceId) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const result = await acknowledgeNotification(organizationId, workspaceId, notificationUuid, authContext, consentClient);
|
|
51
|
+
return result !== null;
|
|
52
|
+
}, [organizationId, workspaceId, authContext, consentClient]);
|
|
53
|
+
|
|
54
|
+
const handleCTAClick = useCallback(async (notification: Notification, cta: NotificationCTA) => {
|
|
55
|
+
if (cta.action_type === 'acknowledge') {
|
|
56
|
+
const ok = await handleAcknowledge(notification.uuid);
|
|
57
|
+
if (!ok) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return 'acknowledged' as const;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (cta.action_type !== 'link' || !cta.url) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!notification.is_read) {
|
|
68
|
+
await handleMarkRead(notification.uuid);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const caseUuid = cta.url.startsWith(CASE_DETAILS_PREFIX)
|
|
73
|
+
? cta.url.slice(CASE_DETAILS_PREFIX.length)
|
|
74
|
+
: '';
|
|
75
|
+
if (caseUuid && organizationId && workspaceId) {
|
|
76
|
+
const contact = typeof window !== 'undefined' ? sessionStorage.getItem('email') || '' : '';
|
|
77
|
+
const caseHistory = await fetchCaseHistory(organizationId, workspaceId, contact, undefined, undefined, authContext, consentClient, langParam);
|
|
78
|
+
const fullCase = caseHistory?.data?.find((c) => c.uuid === caseUuid);
|
|
79
|
+
if (fullCase) {
|
|
80
|
+
setSelectedCase(fullCase);
|
|
81
|
+
navigateTo('case-details');
|
|
82
|
+
return 'navigated' as const;
|
|
83
|
+
}
|
|
84
|
+
return 'not_found' as const;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let parsed: URL;
|
|
88
|
+
try {
|
|
89
|
+
parsed = new URL(cta.url);
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
window.open(parsed.href, '_blank', 'noopener,noreferrer');
|
|
97
|
+
return 'opened' as const;
|
|
98
|
+
}, [handleAcknowledge, handleMarkRead, navigateTo, setSelectedCase, organizationId, workspaceId, authContext, consentClient, langParam]);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
organizationId,
|
|
102
|
+
workspaceId,
|
|
103
|
+
authContext,
|
|
104
|
+
consentClient,
|
|
105
|
+
navigateTo,
|
|
106
|
+
hasAcknowledgeCTA,
|
|
107
|
+
handleMarkRead,
|
|
108
|
+
handleMarkAllRead,
|
|
109
|
+
handleAcknowledge,
|
|
110
|
+
handleCTAClick,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TFunction } from 'i18next';
|
|
2
|
+
|
|
3
|
+
export const formatTimeAgo = (dateString: string, t: TFunction): string => {
|
|
4
|
+
const now = new Date();
|
|
5
|
+
const date = new Date(dateString);
|
|
6
|
+
if (Number.isNaN(date.getTime())) {
|
|
7
|
+
return t('justNow')
|
|
8
|
+
};
|
|
9
|
+
const diffMs = now.getTime() - date.getTime();
|
|
10
|
+
const diffMin = Math.floor(diffMs / 60_000);
|
|
11
|
+
if (diffMin < 1) {
|
|
12
|
+
return t('justNow')
|
|
13
|
+
};
|
|
14
|
+
if (diffMin < 60) {
|
|
15
|
+
return t('minutesAgo', { count: diffMin })
|
|
16
|
+
};
|
|
17
|
+
const diffHrs = Math.floor(diffMin / 60);
|
|
18
|
+
if (diffHrs < 24) {
|
|
19
|
+
return t('hoursAgo', { count: diffHrs })
|
|
20
|
+
};
|
|
21
|
+
const diffDays = Math.floor(diffHrs / 24);
|
|
22
|
+
if (diffDays < 7) {
|
|
23
|
+
return t('daysAgo', { count: diffDays })
|
|
24
|
+
};
|
|
25
|
+
const diffWeeks = Math.floor(diffDays / 7);
|
|
26
|
+
return t('weeksAgo', { count: diffWeeks });
|
|
27
|
+
};
|
|
@@ -12,8 +12,9 @@ import { usePageChrome } from '../context/PageChromeContext';
|
|
|
12
12
|
import i18n from '../lib/i18n';
|
|
13
13
|
import { pcStyles } from '../styles/pcStyles';
|
|
14
14
|
import type { PrivacyCenterLayoutProps } from './types';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import { NotificationBell } from './NotificationHub/NotificationBell';
|
|
16
|
+
import { getVar } from '../lib/utils';
|
|
17
|
+
import { Globe2 } from 'lucide-react';
|
|
17
18
|
|
|
18
19
|
export default function PrivacyCenterLayout({
|
|
19
20
|
children,
|
|
@@ -23,7 +24,7 @@ export default function PrivacyCenterLayout({
|
|
|
23
24
|
}: PrivacyCenterLayoutProps) {
|
|
24
25
|
const { t } = useTranslation();
|
|
25
26
|
const { currentPage } = useInternalNavigation();
|
|
26
|
-
const { pageChromeOverride } = usePageChrome();
|
|
27
|
+
const { pageChromeOverride, headerStatus } = usePageChrome();
|
|
27
28
|
const [locale, setLocale] = useState(i18n.language);
|
|
28
29
|
const pageChrome = pageChromeOverride ?? PAGE_CHROME_BY_PAGE[currentPage];
|
|
29
30
|
|
|
@@ -59,12 +60,11 @@ export default function PrivacyCenterLayout({
|
|
|
59
60
|
flex: 1,
|
|
60
61
|
minWidth: 0,
|
|
61
62
|
backgroundColor: getVar('--pc-bg-shell'),
|
|
63
|
+
minHeight: 0,
|
|
62
64
|
}} className="pc-content-wrapper">
|
|
63
65
|
{/* Top Navbar with Theme Toggle */}
|
|
64
66
|
<nav style={{
|
|
65
|
-
position: '
|
|
66
|
-
top: 0,
|
|
67
|
-
zIndex: 50,
|
|
67
|
+
position: 'relative',
|
|
68
68
|
...pcStyles.flex,
|
|
69
69
|
...pcStyles.itemsCenter,
|
|
70
70
|
...pcStyles.justifyBetween,
|
|
@@ -74,8 +74,8 @@ export default function PrivacyCenterLayout({
|
|
|
74
74
|
paddingRight: 'var(--pc-navbar-padding-right, 0.75rem)',
|
|
75
75
|
paddingTop: 'var(--pc-navbar-padding-top, 0.625rem)',
|
|
76
76
|
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
|
|
77
|
+
transition: 'background-color 0.3s ease, border-color 0.3s ease',
|
|
78
|
+
willChange: 'background-color, border-color',
|
|
79
79
|
}} className="pc-navbar">
|
|
80
80
|
<div
|
|
81
81
|
className="pc-navbar-page-meta"
|
|
@@ -85,32 +85,76 @@ export default function PrivacyCenterLayout({
|
|
|
85
85
|
justifyContent: 'center',
|
|
86
86
|
minWidth: 0,
|
|
87
87
|
flex: 1,
|
|
88
|
-
gap: '0.
|
|
88
|
+
gap: '0.1875rem',
|
|
89
89
|
}}
|
|
90
90
|
>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem', minWidth: 0 }}>
|
|
92
|
+
<p
|
|
93
|
+
className="pc-navbar-title"
|
|
94
|
+
style={{
|
|
95
|
+
margin: 0,
|
|
96
|
+
color: getVar('--pc-text-primary'),
|
|
97
|
+
fontSize: '1.0625rem',
|
|
98
|
+
fontWeight: 700,
|
|
99
|
+
lineHeight: 1.2,
|
|
100
|
+
whiteSpace: 'nowrap',
|
|
101
|
+
overflow: 'hidden',
|
|
102
|
+
textOverflow: 'ellipsis',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{t(pageChrome.titleKey)}
|
|
106
|
+
</p>
|
|
107
|
+
{headerStatus && (
|
|
108
|
+
<span
|
|
109
|
+
style={{
|
|
110
|
+
display: 'inline-flex',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
flexShrink: 0,
|
|
113
|
+
borderRadius: '9999px',
|
|
114
|
+
gap: '0.375rem',
|
|
115
|
+
padding: '0.25rem 0.75rem',
|
|
116
|
+
fontSize: '0.625rem',
|
|
117
|
+
fontWeight: 600,
|
|
118
|
+
lineHeight: 1.2,
|
|
119
|
+
border: `1px solid ${
|
|
120
|
+
headerStatus.variant === 'success'
|
|
121
|
+
? 'rgba(16, 185, 129, 0.35)'
|
|
122
|
+
: 'rgba(245, 158, 11, 0.35)'
|
|
123
|
+
}`,
|
|
124
|
+
backgroundColor:
|
|
125
|
+
headerStatus.variant === 'success'
|
|
126
|
+
? 'rgba(16, 185, 129, 0.1)'
|
|
127
|
+
: '#fff7ed',
|
|
128
|
+
color:
|
|
129
|
+
headerStatus.variant === 'success'
|
|
130
|
+
? getVar('--pc-success')
|
|
131
|
+
: '#d97706',
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<span
|
|
135
|
+
aria-hidden="true"
|
|
136
|
+
style={{
|
|
137
|
+
width: '0.4375rem',
|
|
138
|
+
height: '0.4375rem',
|
|
139
|
+
borderRadius: '9999px',
|
|
140
|
+
backgroundColor:
|
|
141
|
+
headerStatus.variant === 'success'
|
|
142
|
+
? getVar('--pc-success')
|
|
143
|
+
: '#d97706',
|
|
144
|
+
}}
|
|
145
|
+
/>
|
|
146
|
+
{headerStatus.label}
|
|
147
|
+
</span>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
106
150
|
{pageChrome.descriptionKey && (
|
|
107
151
|
<p
|
|
108
152
|
className="pc-navbar-description"
|
|
109
153
|
style={{
|
|
110
154
|
margin: 0,
|
|
111
155
|
color: getVar('--pc-text-secondary'),
|
|
112
|
-
fontSize: '0.
|
|
113
|
-
lineHeight: 1.
|
|
156
|
+
fontSize: '0.75rem',
|
|
157
|
+
lineHeight: 1.2,
|
|
114
158
|
whiteSpace: 'nowrap',
|
|
115
159
|
overflow: 'hidden',
|
|
116
160
|
textOverflow: 'ellipsis',
|
|
@@ -123,52 +167,57 @@ export default function PrivacyCenterLayout({
|
|
|
123
167
|
<div style={{
|
|
124
168
|
display: 'flex',
|
|
125
169
|
alignItems: 'center',
|
|
126
|
-
gap: '0.
|
|
170
|
+
gap: '0.375rem',
|
|
127
171
|
flexShrink: 0,
|
|
128
172
|
}} className="pc-navbar-actions">
|
|
129
173
|
<PCSelect
|
|
174
|
+
className="pc-navbar-language-select"
|
|
130
175
|
size="sm"
|
|
131
176
|
searchable
|
|
177
|
+
prefix={<Globe2 size={12} strokeWidth={1.8} />}
|
|
132
178
|
searchPlaceholder={t('searchPlaceholder')}
|
|
133
179
|
noOptionsMessage={t('noOptionsFound')}
|
|
134
180
|
style={{
|
|
135
181
|
width: 'auto',
|
|
136
|
-
minWidth: '
|
|
137
|
-
maxWidth: '
|
|
182
|
+
minWidth: '120px',
|
|
183
|
+
maxWidth: 'min(65vw, 320px)',
|
|
138
184
|
flexShrink: 0,
|
|
139
185
|
}}
|
|
140
186
|
options={languageOptions}
|
|
141
187
|
value={languageOptions.find((opt) => opt.value === locale) || null}
|
|
142
188
|
onChange={handleLanguageChange}
|
|
143
189
|
/>
|
|
144
|
-
<
|
|
190
|
+
<NotificationBell />
|
|
191
|
+
<ThemeToggle className="pc-navbar-icon-btn" />
|
|
145
192
|
</div>
|
|
146
193
|
</nav>
|
|
147
194
|
<div style={{
|
|
148
195
|
...pcStyles.flex,
|
|
149
196
|
...pcStyles.flexCol,
|
|
150
|
-
|
|
197
|
+
flex: 1,
|
|
198
|
+
minHeight: 0,
|
|
151
199
|
paddingLeft: 'var(--pc-content-padding-left, 0.75rem)',
|
|
152
200
|
paddingRight: 'var(--pc-content-padding-right, 0.75rem)',
|
|
153
|
-
paddingTop: 'var(--pc-content-padding-top,
|
|
201
|
+
paddingTop: 'var(--pc-content-padding-top, 0)',
|
|
154
202
|
minWidth: 0,
|
|
155
203
|
backgroundColor: getVar('--pc-bg-shell'),
|
|
156
204
|
}} className="pc-content-container">
|
|
157
205
|
<div style={{
|
|
158
|
-
|
|
206
|
+
display: 'flex',
|
|
207
|
+
flexDirection: 'column',
|
|
208
|
+
flex: 1,
|
|
209
|
+
minHeight: 0,
|
|
159
210
|
width: '100%',
|
|
160
211
|
}}>
|
|
161
212
|
<div style={{
|
|
162
213
|
...pcStyles.flex,
|
|
163
214
|
...pcStyles.flexCol,
|
|
164
|
-
|
|
165
|
-
minHeight:
|
|
215
|
+
flex: 1,
|
|
216
|
+
minHeight: 0,
|
|
166
217
|
...pcStyles.justifyBetween,
|
|
167
|
-
...pcStyles.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
paddingTop: '1rem',
|
|
171
|
-
paddingBottom: '1rem',
|
|
218
|
+
...pcStyles.gapSm,
|
|
219
|
+
paddingTop: '0.625rem',
|
|
220
|
+
paddingBottom: '0.75rem',
|
|
172
221
|
}}>
|
|
173
222
|
<div className={contentClassName} style={{ width: '100%' }}>{children}</div>
|
|
174
223
|
<div style={{
|
|
@@ -198,17 +247,14 @@ export default function PrivacyCenterLayout({
|
|
|
198
247
|
.pc-navbar {
|
|
199
248
|
--pc-navbar-padding-left: 0.75rem;
|
|
200
249
|
--pc-navbar-padding-right: 0.75rem;
|
|
201
|
-
--pc-navbar-padding-top: 0.
|
|
202
|
-
--pc-navbar-padding-bottom: 0.
|
|
203
|
-
left: var(--pc-sidebar-offset) !important;
|
|
204
|
-
width: calc(100% - var(--pc-sidebar-offset)) !important;
|
|
205
|
-
transition: left 0.3s ease, width 0.3s ease !important;
|
|
250
|
+
--pc-navbar-padding-top: 0.875rem;
|
|
251
|
+
--pc-navbar-padding-bottom: 0.75rem;
|
|
206
252
|
}
|
|
207
253
|
|
|
208
254
|
.pc-content-container {
|
|
209
255
|
--pc-content-padding-left: 0.75rem;
|
|
210
256
|
--pc-content-padding-right: 0.75rem;
|
|
211
|
-
--pc-content-padding-top:
|
|
257
|
+
--pc-content-padding-top: 0;
|
|
212
258
|
}
|
|
213
259
|
|
|
214
260
|
.pc-navbar-page-meta {
|
|
@@ -246,7 +292,7 @@ export default function PrivacyCenterLayout({
|
|
|
246
292
|
.pc-navbar {
|
|
247
293
|
--pc-navbar-padding-left: 3.75rem;
|
|
248
294
|
--pc-navbar-padding-right: 0.75rem;
|
|
249
|
-
--pc-navbar-padding-top: 0.
|
|
295
|
+
--pc-navbar-padding-top: 0.75rem;
|
|
250
296
|
--pc-navbar-padding-bottom: 0.625rem;
|
|
251
297
|
}
|
|
252
298
|
.pc-navbar-description {
|
|
@@ -260,21 +306,21 @@ export default function PrivacyCenterLayout({
|
|
|
260
306
|
gap: 0.375rem;
|
|
261
307
|
}
|
|
262
308
|
.pc-content-container {
|
|
263
|
-
--pc-content-padding-top:
|
|
309
|
+
--pc-content-padding-top: 0;
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
@media (min-width: 768px) {
|
|
267
313
|
.pc-navbar {
|
|
268
|
-
--pc-navbar-padding-left:
|
|
269
|
-
--pc-navbar-padding-right:
|
|
270
|
-
--pc-navbar-padding-top: 0.
|
|
314
|
+
--pc-navbar-padding-left: 1.25rem;
|
|
315
|
+
--pc-navbar-padding-right: 1.25rem;
|
|
316
|
+
--pc-navbar-padding-top: 0.875rem;
|
|
271
317
|
--pc-navbar-padding-bottom: 0.75rem;
|
|
272
318
|
}
|
|
273
319
|
.pc-navbar-description {
|
|
274
320
|
display: block;
|
|
275
321
|
}
|
|
276
322
|
.pc-content-container {
|
|
277
|
-
--pc-content-padding-top:
|
|
323
|
+
--pc-content-padding-top: 0;
|
|
278
324
|
}
|
|
279
325
|
}
|
|
280
326
|
@media (min-width: 768px) {
|
|
@@ -282,19 +328,23 @@ export default function PrivacyCenterLayout({
|
|
|
282
328
|
font-size: 1.0625rem;
|
|
283
329
|
}
|
|
284
330
|
.pc-navbar-description {
|
|
285
|
-
font-size: 0.
|
|
331
|
+
font-size: 0.6875rem;
|
|
286
332
|
}
|
|
287
333
|
.pc-content-container {
|
|
288
334
|
--pc-content-padding-left: 1.25rem;
|
|
289
335
|
--pc-content-padding-right: 1.25rem;
|
|
290
|
-
--pc-content-padding-top:
|
|
336
|
+
--pc-content-padding-top: 0;
|
|
291
337
|
}
|
|
292
338
|
}
|
|
293
339
|
@media (min-width: 1024px) {
|
|
340
|
+
.pc-navbar {
|
|
341
|
+
--pc-navbar-padding-left: 1.5rem;
|
|
342
|
+
--pc-navbar-padding-right: 1.5rem;
|
|
343
|
+
}
|
|
294
344
|
.pc-content-container {
|
|
295
345
|
--pc-content-padding-left: 1.5rem;
|
|
296
346
|
--pc-content-padding-right: 1.5rem;
|
|
297
|
-
--pc-content-padding-top:
|
|
347
|
+
--pc-content-padding-top: 0;
|
|
298
348
|
}
|
|
299
349
|
}
|
|
300
350
|
`}</style>
|
|
@@ -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
|
-
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
|
|
2
2
|
import type { CaseRequest } from '../lib/types';
|
|
3
3
|
|
|
4
|
-
export type InternalPage = 'consent-manager' | 'form' | 'activity' | 'case-details';
|
|
4
|
+
export type InternalPage = 'consent-manager' | 'form' | 'activity' | 'case-details' | 'notifications';
|
|
5
5
|
|
|
6
6
|
const NAV_STORAGE_KEY = 'pc_current_page';
|
|
7
7
|
const CASE_UUID_STORAGE_KEY = 'pc_selected_case_uuid';
|
|
8
8
|
|
|
9
9
|
const VALID_PAGES: ReadonlySet<string> = new Set<InternalPage>([
|
|
10
|
-
'consent-manager', 'form', 'activity', 'case-details',
|
|
10
|
+
'consent-manager', 'form', 'activity', 'case-details', 'notifications',
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
13
|
function getStoredPage(fallback: InternalPage): InternalPage {
|
|
@@ -12,10 +12,17 @@ export type PageChromeOverride = {
|
|
|
12
12
|
descriptionKey?: string;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
export type PageHeaderStatus = {
|
|
16
|
+
label: string;
|
|
17
|
+
variant: 'success' | 'warning';
|
|
18
|
+
};
|
|
19
|
+
|
|
15
20
|
type PageChromeContextValue = {
|
|
16
21
|
pageChromeOverride: PageChromeOverride | null;
|
|
17
22
|
setPageChromeOverride: (override: PageChromeOverride | null) => void;
|
|
18
23
|
clearPageChromeOverride: () => void;
|
|
24
|
+
headerStatus: PageHeaderStatus | null;
|
|
25
|
+
setHeaderStatus: (status: PageHeaderStatus | null) => void;
|
|
19
26
|
};
|
|
20
27
|
|
|
21
28
|
const PageChromeContext = createContext<PageChromeContextValue | null>(null);
|
|
@@ -23,6 +30,7 @@ const PageChromeContext = createContext<PageChromeContextValue | null>(null);
|
|
|
23
30
|
export function PageChromeProvider({ children }: { children: ReactNode }) {
|
|
24
31
|
const [pageChromeOverride, setPageChromeOverrideState] =
|
|
25
32
|
useState<PageChromeOverride | null>(null);
|
|
33
|
+
const [headerStatus, setHeaderStatusState] = useState<PageHeaderStatus | null>(null);
|
|
26
34
|
|
|
27
35
|
const setPageChromeOverride = useCallback(
|
|
28
36
|
(override: PageChromeOverride | null) => {
|
|
@@ -35,13 +43,25 @@ export function PageChromeProvider({ children }: { children: ReactNode }) {
|
|
|
35
43
|
setPageChromeOverrideState(null);
|
|
36
44
|
}, []);
|
|
37
45
|
|
|
46
|
+
const setHeaderStatus = useCallback((status: PageHeaderStatus | null) => {
|
|
47
|
+
setHeaderStatusState(status);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
38
50
|
const value = useMemo(
|
|
39
51
|
() => ({
|
|
40
52
|
pageChromeOverride,
|
|
41
53
|
setPageChromeOverride,
|
|
42
54
|
clearPageChromeOverride,
|
|
55
|
+
headerStatus,
|
|
56
|
+
setHeaderStatus,
|
|
43
57
|
}),
|
|
44
|
-
[
|
|
58
|
+
[
|
|
59
|
+
pageChromeOverride,
|
|
60
|
+
setPageChromeOverride,
|
|
61
|
+
clearPageChromeOverride,
|
|
62
|
+
headerStatus,
|
|
63
|
+
setHeaderStatus,
|
|
64
|
+
]
|
|
45
65
|
);
|
|
46
66
|
|
|
47
67
|
return (
|