@redacto.io/consent-sdk-react 3.4.0 → 3.6.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/CHANGELOG.md +12 -0
- package/LICENSE +176 -0
- package/dist/index.d.mts +51 -4
- package/dist/index.d.ts +51 -4
- package/dist/index.js +1912 -345
- package/dist/index.mjs +1911 -345
- package/dist/privacy-center.d.mts +566 -0
- package/dist/privacy-center.d.ts +566 -0
- package/dist/privacy-center.js +17174 -0
- package/dist/privacy-center.mjs +17136 -0
- package/package.json +47 -3
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +201 -118
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1194 -93
- package/src/RedactoNoticeConsent/api/index.ts +337 -246
- package/src/RedactoNoticeConsent/api/types.ts +140 -41
- package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +602 -0
- package/src/RedactoNoticeConsent/components/index.ts +7 -0
- package/src/RedactoNoticeConsent/injectStyles.ts +11 -0
- package/src/RedactoNoticeConsent/language-codes.ts +61 -0
- package/src/RedactoNoticeConsent/services/storage.ts +122 -0
- package/src/RedactoNoticeConsent/types.ts +2 -0
- package/src/RedactoNoticeConsentInline/api/index.ts +26 -64
- package/src/RedactoNoticeConsentInline/api/types.ts +0 -25
- package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +27 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +142 -0
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +51 -0
- package/src/RedactoPrivacyCenter/api/actions.ts +444 -0
- package/src/RedactoPrivacyCenter/api/client.ts +413 -0
- package/src/RedactoPrivacyCenter/api/config.ts +33 -0
- package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
- package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
- package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
- package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
- package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
- package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
- package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
- package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
- package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +468 -0
- package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +433 -0
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +726 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +239 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
- package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +305 -0
- package/src/RedactoPrivacyCenter/components/Form.tsx +1455 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +176 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +330 -0
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +329 -0
- package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +302 -0
- package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +218 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +412 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
- package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
- package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
- package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +179 -0
- package/src/RedactoPrivacyCenter/components/constant.ts +20 -0
- package/src/RedactoPrivacyCenter/components/types.ts +68 -0
- package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
- package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
- package/src/RedactoPrivacyCenter/config/table.ts +25 -0
- package/src/RedactoPrivacyCenter/context/AuthContext.tsx +123 -0
- package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +44 -0
- package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
- package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +64 -0
- package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
- package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
- package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
- package/src/RedactoPrivacyCenter/lib/api.ts +468 -0
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
- package/src/RedactoPrivacyCenter/lib/constants.ts +116 -0
- package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
- package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
- package/src/RedactoPrivacyCenter/lib/queries.ts +30 -0
- package/src/RedactoPrivacyCenter/lib/types.ts +346 -0
- package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
- package/src/RedactoPrivacyCenter/lib/utils.ts +625 -0
- package/src/RedactoPrivacyCenter/locales/as/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/en/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/or/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/te/translation.json +188 -0
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +188 -0
- package/src/RedactoPrivacyCenter/styles/base.css +117 -0
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1000 -0
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +819 -0
- package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
- package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +365 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +479 -0
- package/src/RedactoPrivacyCenter/table/index.ts +2 -0
- package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +73 -0
- package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
- package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +87 -0
- package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
- package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
- package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
- package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
- package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
- package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +97 -0
- package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +128 -0
- package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +30 -0
- package/src/RedactoPrivacyCenter/ui/PCModal.tsx +104 -0
- package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +175 -0
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +381 -0
- package/src/RedactoPrivacyCenter/ui/PCTable.tsx +137 -0
- package/src/RedactoPrivacyCenter/ui/PCText.tsx +39 -0
- package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
- package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
- package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
- package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
- package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +55 -0
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +70 -0
- package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
- package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
- package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
- package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
- package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
- package/src/index.ts +10 -0
- package/src/privacy-center.ts +107 -0
- package/tsconfig.json +2 -0
- package/tsup.config.ts +16 -1
- package/src/utils/isLegacyApi.ts +0 -12
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { createColumnHelper } from '@tanstack/react-table';
|
|
4
|
+
import { PCText, PCTooltip } from '../ui';
|
|
5
|
+
import { pcStyles } from '../styles/pcStyles';
|
|
6
|
+
import dayjs from 'dayjs';
|
|
7
|
+
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
8
|
+
import type { Activity } from '../lib/types';
|
|
9
|
+
import StatusBadge from '../ui/StatusBadge';
|
|
10
|
+
import { getActivityTypeStatus, formatActivityType } from '../lib/utils';
|
|
11
|
+
import {
|
|
12
|
+
ACTIVITY_COLUMN_SIZES,
|
|
13
|
+
ACTIVITY_DESCRIPTION_TRUNCATE_LENGTH,
|
|
14
|
+
ACTIVITY_DATE_FORMATS,
|
|
15
|
+
} from '../lib/constants';
|
|
16
|
+
|
|
17
|
+
dayjs.extend(relativeTime);
|
|
18
|
+
|
|
19
|
+
const columnHelper = createColumnHelper<Activity>();
|
|
20
|
+
|
|
21
|
+
export const useActivitiesColumns = () => {
|
|
22
|
+
const { t } = useTranslation();
|
|
23
|
+
return useMemo(
|
|
24
|
+
() => [
|
|
25
|
+
columnHelper.accessor('title', {
|
|
26
|
+
id: 'title',
|
|
27
|
+
minSize: ACTIVITY_COLUMN_SIZES.TITLE,
|
|
28
|
+
header: t('activity'),
|
|
29
|
+
cell: (info) => {
|
|
30
|
+
const row = info.row.original;
|
|
31
|
+
const title = row.title_display || info.getValue();
|
|
32
|
+
return (
|
|
33
|
+
<div style={{ paddingTop: pcStyles.pySm.paddingTop, paddingBottom: pcStyles.pySm.paddingBottom }}>
|
|
34
|
+
<PCText size="sm" weight="medium" color="primary">
|
|
35
|
+
{title}
|
|
36
|
+
</PCText>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
enableSorting: false,
|
|
41
|
+
enableColumnFilter: false,
|
|
42
|
+
}),
|
|
43
|
+
columnHelper.accessor('activity_type', {
|
|
44
|
+
id: 'activity_type',
|
|
45
|
+
minSize: ACTIVITY_COLUMN_SIZES.ACTIVITY_TYPE,
|
|
46
|
+
header: t('activityType'),
|
|
47
|
+
cell: (info) => {
|
|
48
|
+
const activityType = info.getValue();
|
|
49
|
+
const row = info.row.original;
|
|
50
|
+
const displayType = row.activity_type_display || formatActivityType(activityType);
|
|
51
|
+
const status = getActivityTypeStatus(activityType);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div style={{ paddingTop: pcStyles.pySm.paddingTop, paddingBottom: pcStyles.pySm.paddingBottom }}>
|
|
55
|
+
<StatusBadge status={status} variant="filled" className="pc-status-badge">
|
|
56
|
+
{displayType}
|
|
57
|
+
</StatusBadge>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
enableSorting: false,
|
|
62
|
+
enableColumnFilter: false,
|
|
63
|
+
}),
|
|
64
|
+
columnHelper.accessor('description', {
|
|
65
|
+
id: 'description',
|
|
66
|
+
minSize: ACTIVITY_COLUMN_SIZES.DESCRIPTION,
|
|
67
|
+
header: t('activityDescription'),
|
|
68
|
+
cell: (info) => {
|
|
69
|
+
const row = info.row.original;
|
|
70
|
+
const description = row.description_display || info.getValue();
|
|
71
|
+
const truncatedDescription =
|
|
72
|
+
description.length > ACTIVITY_DESCRIPTION_TRUNCATE_LENGTH
|
|
73
|
+
? `${description.substring(0, ACTIVITY_DESCRIPTION_TRUNCATE_LENGTH)}...`
|
|
74
|
+
: description;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div style={{ paddingTop: pcStyles.pySm.paddingTop, paddingBottom: pcStyles.pySm.paddingBottom }}>
|
|
78
|
+
{description.length > ACTIVITY_DESCRIPTION_TRUNCATE_LENGTH ? (
|
|
79
|
+
<PCTooltip content={description} placement="top">
|
|
80
|
+
<span style={{ display: 'block', cursor: 'help' }}>
|
|
81
|
+
<PCText size="sm" color="secondary" style={{
|
|
82
|
+
display: '-webkit-box',
|
|
83
|
+
WebkitLineClamp: 2,
|
|
84
|
+
WebkitBoxOrient: 'vertical',
|
|
85
|
+
overflow: 'hidden',
|
|
86
|
+
}}>
|
|
87
|
+
{truncatedDescription}
|
|
88
|
+
</PCText>
|
|
89
|
+
</span>
|
|
90
|
+
</PCTooltip>
|
|
91
|
+
) : (
|
|
92
|
+
<PCText size="sm" color="secondary">
|
|
93
|
+
{description}
|
|
94
|
+
</PCText>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
enableSorting: false,
|
|
100
|
+
enableColumnFilter: false,
|
|
101
|
+
}),
|
|
102
|
+
columnHelper.accessor('timestamp', {
|
|
103
|
+
id: 'timestamp',
|
|
104
|
+
minSize: ACTIVITY_COLUMN_SIZES.TIMESTAMP,
|
|
105
|
+
header: t('timestamp'),
|
|
106
|
+
cell: (info) => {
|
|
107
|
+
const timestamp = info.getValue();
|
|
108
|
+
const date = dayjs(timestamp);
|
|
109
|
+
const formattedDateTime = date.format(ACTIVITY_DATE_FORMATS.DISPLAY);
|
|
110
|
+
const relativeTime = date.fromNow();
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div style={{ paddingTop: pcStyles.pySm.paddingTop, paddingBottom: pcStyles.pySm.paddingBottom }}>
|
|
114
|
+
<PCTooltip
|
|
115
|
+
content={date.format(ACTIVITY_DATE_FORMATS.TOOLTIP)}
|
|
116
|
+
placement="top"
|
|
117
|
+
>
|
|
118
|
+
<div style={{
|
|
119
|
+
...pcStyles.flex,
|
|
120
|
+
...pcStyles.flexCol,
|
|
121
|
+
minWidth: '120px',
|
|
122
|
+
gap: '0.125rem',
|
|
123
|
+
}}>
|
|
124
|
+
<PCText size="sm" weight="medium" color="primary">
|
|
125
|
+
{formattedDateTime}
|
|
126
|
+
</PCText>
|
|
127
|
+
<PCText size="sm" color="secondary" style={{ fontSize: '0.75rem' }}>
|
|
128
|
+
{relativeTime}
|
|
129
|
+
</PCText>
|
|
130
|
+
</div>
|
|
131
|
+
</PCTooltip>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
enableSorting: false,
|
|
136
|
+
enableColumnFilter: false,
|
|
137
|
+
}),
|
|
138
|
+
],
|
|
139
|
+
[t]
|
|
140
|
+
);
|
|
141
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { PCText, PCActionIcon, PCTooltip, PCBadge } from '../ui';
|
|
4
|
+
import { pcStyles } from '../styles/pcStyles';
|
|
5
|
+
import { FiEye, FiDownload } from 'react-icons/fi';
|
|
6
|
+
import { createColumnHelper } from '@tanstack/react-table';
|
|
7
|
+
import type { CaseRequest } from '../lib/types';
|
|
8
|
+
import { formatDate } from './utils';
|
|
9
|
+
|
|
10
|
+
const getVar = (varName: string): string => `var(${varName})`;
|
|
11
|
+
const columnHelper = createColumnHelper<CaseRequest>();
|
|
12
|
+
|
|
13
|
+
const getCaseStatusVariant = (status: string): 'success' | 'error' | 'warning' | 'info' | 'secondary' => {
|
|
14
|
+
const n = status.toLowerCase().replace(/_/g, '');
|
|
15
|
+
if (n === 'completed') return 'success';
|
|
16
|
+
if (n === 'rejected' || n === 'declined') return 'error';
|
|
17
|
+
if (n === 'processing' || n === 'inprogress' || n === 'expired') return 'warning';
|
|
18
|
+
if (n === 'initiated') return 'info';
|
|
19
|
+
return 'secondary';
|
|
20
|
+
};
|
|
21
|
+
const formatCaseStatus = (s: string) =>
|
|
22
|
+
s.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
23
|
+
|
|
24
|
+
export type UseCaseHistoryColumnsProps = {
|
|
25
|
+
onViewDetails: (caseRequest: CaseRequest) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const useCaseHistoryColumns = ({
|
|
29
|
+
onViewDetails,
|
|
30
|
+
}: UseCaseHistoryColumnsProps) => {
|
|
31
|
+
const { t } = useTranslation();
|
|
32
|
+
return useMemo(
|
|
33
|
+
() => [
|
|
34
|
+
columnHelper.accessor('case_id', {
|
|
35
|
+
id: 'case_id',
|
|
36
|
+
size: 150,
|
|
37
|
+
minSize: 120,
|
|
38
|
+
header: t('caseID'),
|
|
39
|
+
meta: { align: 'left' },
|
|
40
|
+
cell: (info) => (
|
|
41
|
+
<div style={{ width: '100%', minWidth: '100px', textAlign: 'left' }}>
|
|
42
|
+
<PCText size="sm" weight="medium" color="primary">
|
|
43
|
+
{info.getValue()}
|
|
44
|
+
</PCText>
|
|
45
|
+
<PCText
|
|
46
|
+
size="sm"
|
|
47
|
+
color="secondary"
|
|
48
|
+
style={{
|
|
49
|
+
marginTop: pcStyles.mtXs.marginTop,
|
|
50
|
+
display: 'block',
|
|
51
|
+
fontSize: '0.75rem',
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{formatDate(info.row.original.created_at)}
|
|
55
|
+
</PCText>
|
|
56
|
+
</div>
|
|
57
|
+
),
|
|
58
|
+
enableSorting: false,
|
|
59
|
+
enableColumnFilter: false,
|
|
60
|
+
}),
|
|
61
|
+
columnHelper.accessor('request_type', {
|
|
62
|
+
id: 'request_type',
|
|
63
|
+
size: 150,
|
|
64
|
+
minSize: 120,
|
|
65
|
+
header: t('requestType'),
|
|
66
|
+
meta: { align: 'left' },
|
|
67
|
+
cell: (info) => {
|
|
68
|
+
const row = info.row.original;
|
|
69
|
+
const displayValue = row.request_type_display || info.getValue().replace('_', ' ');
|
|
70
|
+
return (
|
|
71
|
+
<PCText
|
|
72
|
+
size="sm"
|
|
73
|
+
style={{
|
|
74
|
+
textAlign: 'left',
|
|
75
|
+
textTransform: row.request_type_display ? 'none' : 'capitalize',
|
|
76
|
+
width: '100%',
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{displayValue}
|
|
80
|
+
</PCText>
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
enableSorting: false,
|
|
84
|
+
enableColumnFilter: false,
|
|
85
|
+
}),
|
|
86
|
+
columnHelper.accessor('status', {
|
|
87
|
+
id: 'status',
|
|
88
|
+
size: 120,
|
|
89
|
+
minSize: 100,
|
|
90
|
+
header: t('status'),
|
|
91
|
+
meta: { align: 'left' },
|
|
92
|
+
cell: (info) => {
|
|
93
|
+
const row = info.row.original;
|
|
94
|
+
const displayStatus = row.status_display || formatCaseStatus(info.getValue());
|
|
95
|
+
return (
|
|
96
|
+
<div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter, ...pcStyles.justifyStart, width: '100%' }}>
|
|
97
|
+
<PCBadge variant={getCaseStatusVariant(info.getValue())} size="sm" className="pc-status-badge">
|
|
98
|
+
{displayStatus}
|
|
99
|
+
</PCBadge>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
enableSorting: false,
|
|
104
|
+
enableColumnFilter: false,
|
|
105
|
+
}),
|
|
106
|
+
columnHelper.accessor('created_at', {
|
|
107
|
+
id: 'created_at',
|
|
108
|
+
size: 180,
|
|
109
|
+
minSize: 140,
|
|
110
|
+
header: t('submittedOn'),
|
|
111
|
+
meta: { align: 'left' },
|
|
112
|
+
cell: (info) => (
|
|
113
|
+
<PCText
|
|
114
|
+
size="sm"
|
|
115
|
+
color="secondary"
|
|
116
|
+
style={{
|
|
117
|
+
textAlign: 'left',
|
|
118
|
+
fontSize: '0.875rem',
|
|
119
|
+
width: '100%',
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{formatDate(info.getValue())}
|
|
123
|
+
</PCText>
|
|
124
|
+
),
|
|
125
|
+
enableSorting: false,
|
|
126
|
+
enableColumnFilter: false,
|
|
127
|
+
}),
|
|
128
|
+
columnHelper.display({
|
|
129
|
+
id: 'action',
|
|
130
|
+
size: 100,
|
|
131
|
+
minSize: 80,
|
|
132
|
+
header: t('action'),
|
|
133
|
+
meta: { align: 'right' },
|
|
134
|
+
enableSorting: false,
|
|
135
|
+
enableColumnFilter: false,
|
|
136
|
+
cell: ({ row }) => {
|
|
137
|
+
return (
|
|
138
|
+
<div style={{
|
|
139
|
+
...pcStyles.flex,
|
|
140
|
+
...pcStyles.justifyEnd,
|
|
141
|
+
...pcStyles.gapXs,
|
|
142
|
+
width: '100%',
|
|
143
|
+
}}>
|
|
144
|
+
<PCTooltip content={t('viewDetails')}>
|
|
145
|
+
<PCActionIcon
|
|
146
|
+
variant="outline"
|
|
147
|
+
size="sm"
|
|
148
|
+
style={{
|
|
149
|
+
color: getVar('--pc-text-secondary'),
|
|
150
|
+
transition: 'color 0.2s ease',
|
|
151
|
+
}}
|
|
152
|
+
onClick={() => onViewDetails(row.original)}
|
|
153
|
+
aria-label={t('viewDetails')}
|
|
154
|
+
>
|
|
155
|
+
<FiEye size={16} />
|
|
156
|
+
</PCActionIcon>
|
|
157
|
+
</PCTooltip>
|
|
158
|
+
<PCTooltip content={t('downloadResponse')}>
|
|
159
|
+
<PCActionIcon
|
|
160
|
+
variant="outline"
|
|
161
|
+
size="sm"
|
|
162
|
+
disabled={true}
|
|
163
|
+
style={{
|
|
164
|
+
color: getVar('--pc-text-secondary'),
|
|
165
|
+
opacity: 0.5,
|
|
166
|
+
}}
|
|
167
|
+
aria-label={t('downloadResponse')}
|
|
168
|
+
>
|
|
169
|
+
<FiDownload size={16} />
|
|
170
|
+
</PCActionIcon>
|
|
171
|
+
</PCTooltip>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
],
|
|
177
|
+
[onViewDetails, t]
|
|
178
|
+
);
|
|
179
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TABS = {
|
|
2
|
+
ALL: 'All Requests',
|
|
3
|
+
PROCESSING: 'Processing',
|
|
4
|
+
COMPLETED: 'Completed',
|
|
5
|
+
REJECTED: 'Rejected',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const GRIEVANCE_TAB_IDS = {
|
|
9
|
+
ALL: 'all',
|
|
10
|
+
PROCESSING: 'processing',
|
|
11
|
+
COMPLETED: 'completed',
|
|
12
|
+
REJECTED: 'rejected',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
// Re-export shared table configuration
|
|
16
|
+
export {
|
|
17
|
+
PAGE_SIZE_OPTIONS,
|
|
18
|
+
INITIAL_PAGE,
|
|
19
|
+
INITIAL_PAGE_SIZE,
|
|
20
|
+
} from '../config/table';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import type { CaseRequest, UserConsent } from '../lib/types';
|
|
3
|
+
|
|
4
|
+
export type PrivacyCenterLayoutProps = {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
sidebarClassName?: string;
|
|
7
|
+
contentClassName?: string;
|
|
8
|
+
onBack?: 'back' | 'signout';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type CaseHistoryProps = {
|
|
12
|
+
onCreateRequest: () => void;
|
|
13
|
+
isPopup?: boolean;
|
|
14
|
+
onClose?: () => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CaseDetailsModalProps = {
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
selectedCase: CaseRequest | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type RequestSubmitProps = {
|
|
24
|
+
caseId: string;
|
|
25
|
+
contact: string;
|
|
26
|
+
onCreateRequest: () => void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type PaginationProps = {
|
|
30
|
+
currentPage: number;
|
|
31
|
+
totalPages: number;
|
|
32
|
+
totalRecords: number;
|
|
33
|
+
pageSize: number;
|
|
34
|
+
onPageChange: (page: number) => void;
|
|
35
|
+
onPageSizeChange: (size: number) => void;
|
|
36
|
+
className?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type StatusFilterOption = {
|
|
40
|
+
label: string;
|
|
41
|
+
value: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type ProductFilterOption = {
|
|
45
|
+
label: string;
|
|
46
|
+
value: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ConsentManagerProps = {
|
|
50
|
+
variant?: import('rizzui').TableVariantProps;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type ModifyConsentModalProps = {
|
|
54
|
+
isOpen: boolean;
|
|
55
|
+
onClose: () => void;
|
|
56
|
+
selectedRecord: UserConsent | null;
|
|
57
|
+
isLoading: boolean;
|
|
58
|
+
onManageConsent: (
|
|
59
|
+
purposeId: string,
|
|
60
|
+
action: import('../lib/types').ConsentAction,
|
|
61
|
+
nominatorContact?: string,
|
|
62
|
+
dataElementUuids?: string[]
|
|
63
|
+
) => void;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type UseConsentManagerColumnsProps = {
|
|
67
|
+
onOpenModal: (record: UserConsent) => void;
|
|
68
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getStatusColor as getSharedStatusColor } from '../utils/status';
|
|
2
|
+
import { formatDateShort } from '../utils/date';
|
|
3
|
+
|
|
4
|
+
// Re-export shared utilities with case-specific naming
|
|
5
|
+
export const getStatusColor = (status: string) => {
|
|
6
|
+
return getSharedStatusColor({ status });
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// Re-export shared date formatting
|
|
10
|
+
export const formatDate = formatDateShort;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified table and pagination configuration constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_PAGE = 1;
|
|
6
|
+
export const DEFAULT_PAGE_SIZE = 10;
|
|
7
|
+
export const INITIAL_PAGE = 1;
|
|
8
|
+
export const INITIAL_PAGE_SIZE = 10;
|
|
9
|
+
|
|
10
|
+
export const PAGE_SIZE_OPTIONS = [
|
|
11
|
+
{ value: 10, label: '10' },
|
|
12
|
+
{ value: 15, label: '15' },
|
|
13
|
+
{ value: 20, label: '20' },
|
|
14
|
+
{ value: 25, label: '25' },
|
|
15
|
+
{ value: 50, label: '50' },
|
|
16
|
+
{ value: 100, label: '100' },
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export const TABLE_CONFIG = {
|
|
20
|
+
pageSize: DEFAULT_PAGE_SIZE,
|
|
21
|
+
maxDisplayPurposes: 3,
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export const SEARCH_DEBOUNCE_DELAY = 500;
|
|
25
|
+
export const SKELETON_ROW_COUNT = 5;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { createContext, useContext, ReactNode, useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
|
+
import { setRefreshTokenCookie, getCookieValue, decodeJwtToken } from '../lib/utils';
|
|
3
|
+
|
|
4
|
+
export interface AuthContextValue {
|
|
5
|
+
accessToken: string;
|
|
6
|
+
refreshToken: string;
|
|
7
|
+
onError: (error: Error) => boolean;
|
|
8
|
+
// Internal: updates tokens after refresh
|
|
9
|
+
updateTokens: (accessToken: string, refreshToken?: string) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
13
|
+
|
|
14
|
+
interface AuthProviderProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
accessToken: string;
|
|
17
|
+
refreshToken: string;
|
|
18
|
+
onError: (error: Error) => boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extract and store organization/auth data from JWT token to sessionStorage
|
|
23
|
+
* This makes the SDK self-sufficient - it doesn't depend on host app to set these values
|
|
24
|
+
*/
|
|
25
|
+
const syncJwtDataToSessionStorage = (accessToken: string): void => {
|
|
26
|
+
if (typeof window === 'undefined') return;
|
|
27
|
+
|
|
28
|
+
const decoded = decodeJwtToken(accessToken);
|
|
29
|
+
if (!decoded) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Extract email from JWT (contact or primary_email)
|
|
34
|
+
const email = decoded.contact || decoded.primary_email;
|
|
35
|
+
if (email && typeof email === 'string') {
|
|
36
|
+
sessionStorage.setItem('email', email);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Extract organization UUID
|
|
40
|
+
const orgUuid = decoded.organisation_uuid;
|
|
41
|
+
if (orgUuid && typeof orgUuid === 'string') {
|
|
42
|
+
sessionStorage.setItem('pc_org_id', orgUuid);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Extract workspace UUID
|
|
46
|
+
const workspaceUuid = decoded.workspace_uuid;
|
|
47
|
+
if (workspaceUuid && typeof workspaceUuid === 'string') {
|
|
48
|
+
sessionStorage.setItem('pc_workspace_id', workspaceUuid);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Extract organization slug
|
|
52
|
+
const orgSlug = decoded.organisation_slug;
|
|
53
|
+
if (orgSlug && typeof orgSlug === 'string') {
|
|
54
|
+
sessionStorage.setItem('pc_org_slug', orgSlug);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const AuthProvider = ({
|
|
59
|
+
children,
|
|
60
|
+
accessToken: initialAccessToken,
|
|
61
|
+
refreshToken: initialRefreshToken,
|
|
62
|
+
onError,
|
|
63
|
+
}: AuthProviderProps) => {
|
|
64
|
+
const [accessToken, setAccessToken] = useState(initialAccessToken);
|
|
65
|
+
const [refreshToken, setRefreshToken] = useState(initialRefreshToken);
|
|
66
|
+
|
|
67
|
+
// Sync props to state and storage when they change
|
|
68
|
+
// Only sync if tokens are valid non-empty strings
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (initialAccessToken && typeof initialAccessToken === 'string' && initialAccessToken.trim() !== '') {
|
|
71
|
+
setAccessToken(initialAccessToken);
|
|
72
|
+
if (typeof window !== 'undefined') {
|
|
73
|
+
sessionStorage.setItem('access_token', initialAccessToken);
|
|
74
|
+
// Extract and store org/auth data from JWT
|
|
75
|
+
syncJwtDataToSessionStorage(initialAccessToken);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}, [initialAccessToken]);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (initialRefreshToken && typeof initialRefreshToken === 'string' && initialRefreshToken.trim() !== '') {
|
|
82
|
+
setRefreshToken(initialRefreshToken);
|
|
83
|
+
if (typeof window !== 'undefined') {
|
|
84
|
+
setRefreshTokenCookie(initialRefreshToken);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}, [initialRefreshToken]);
|
|
88
|
+
|
|
89
|
+
// Internal method to update tokens after refresh
|
|
90
|
+
const updateTokens = useCallback((newAccessToken: string, newRefreshToken?: string) => {
|
|
91
|
+
setAccessToken(newAccessToken);
|
|
92
|
+
if (newRefreshToken) {
|
|
93
|
+
setRefreshToken(newRefreshToken);
|
|
94
|
+
}
|
|
95
|
+
// Also update storage
|
|
96
|
+
if (typeof window !== 'undefined') {
|
|
97
|
+
sessionStorage.setItem('access_token', newAccessToken);
|
|
98
|
+
// Extract and store org/auth data from JWT
|
|
99
|
+
syncJwtDataToSessionStorage(newAccessToken);
|
|
100
|
+
if (newRefreshToken) {
|
|
101
|
+
setRefreshTokenCookie(newRefreshToken);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
// Memoize context value to prevent unnecessary re-renders
|
|
107
|
+
const value: AuthContextValue = useMemo(() => ({
|
|
108
|
+
accessToken,
|
|
109
|
+
refreshToken,
|
|
110
|
+
onError,
|
|
111
|
+
updateTokens,
|
|
112
|
+
}), [accessToken, refreshToken, onError, updateTokens]);
|
|
113
|
+
|
|
114
|
+
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const useAuth = (): AuthContextValue => {
|
|
118
|
+
const context = useContext(AuthContext);
|
|
119
|
+
if (!context) {
|
|
120
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
121
|
+
}
|
|
122
|
+
return context;
|
|
123
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { createContext, useContext, ReactNode, useMemo } from 'react';
|
|
2
|
+
import { createConsentClient } from '../api/client';
|
|
3
|
+
import type { BaseRequest } from '../api/client';
|
|
4
|
+
|
|
5
|
+
export interface BaseUrlContextValue {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
consentClient: ReturnType<typeof createConsentClient>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const BaseUrlContext = createContext<BaseUrlContextValue | null>(null);
|
|
11
|
+
|
|
12
|
+
export const BaseUrlProvider = ({
|
|
13
|
+
children,
|
|
14
|
+
baseUrl,
|
|
15
|
+
}: {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
baseUrl: string; // Consent server base URL
|
|
18
|
+
}) => {
|
|
19
|
+
const consentClient = useMemo(() => createConsentClient(baseUrl), [baseUrl]);
|
|
20
|
+
|
|
21
|
+
const value: BaseUrlContextValue = useMemo(
|
|
22
|
+
() => ({
|
|
23
|
+
baseUrl,
|
|
24
|
+
consentClient,
|
|
25
|
+
}),
|
|
26
|
+
[baseUrl, consentClient]
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<BaseUrlContext.Provider value={value}>
|
|
31
|
+
{children}
|
|
32
|
+
</BaseUrlContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const useBaseUrl = () => {
|
|
37
|
+
const context = useContext(BaseUrlContext);
|
|
38
|
+
if (!context) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'useBaseUrl must be used within a BaseUrlProvider'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return context;
|
|
44
|
+
};
|