@redacto.io/consent-sdk-react 4.0.0 → 5.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.
- package/CHANGELOG.md +19 -1
- package/LICENSE +176 -0
- package/README.md +98 -1
- package/dist/index.js +231 -96
- package/dist/index.mjs +231 -96
- package/dist/privacy-center.d.mts +649 -0
- package/dist/privacy-center.d.ts +649 -0
- package/dist/privacy-center.js +20824 -0
- package/dist/privacy-center.mjs +20789 -0
- package/package.json +47 -3
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
- package/src/RedactoNoticeConsent/api/index.ts +2 -0
- package/src/RedactoNoticeConsent/api/types.ts +2 -0
- package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
- package/src/RedactoNoticeConsent/language-codes.ts +97 -0
- package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
- package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
- package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
- package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
- package/src/RedactoPrivacyCenter/api/client.ts +524 -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/CaseDetails/AttachmentCard.tsx +38 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
- package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
- package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
- package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
- package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -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 +304 -0
- package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
- package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
- package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
- package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -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 +159 -0
- package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
- package/src/RedactoPrivacyCenter/components/types.ts +76 -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 +137 -0
- package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
- package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
- package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -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/context/types.ts +14 -0
- package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
- package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
- package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
- package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
- package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
- package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
- package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
- package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
- package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
- package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
- package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
- package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
- package/src/RedactoPrivacyCenter/styles/base.css +117 -0
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
- package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
- package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
- package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
- package/src/RedactoPrivacyCenter/table/index.ts +2 -0
- package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
- package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
- package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -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 +107 -0
- package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
- package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
- package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
- package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
- package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
- package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
- package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
- package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -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 +53 -0
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -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/privacy-center.ts +108 -0
- package/tsconfig.json +2 -0
- package/tsup.config.ts +16 -1
- package/.turbo/turbo-build.log +0 -21
|
@@ -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} 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,159 @@
|
|
|
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 { 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 const useCaseHistoryColumns = () => {
|
|
25
|
+
const { t } = useTranslation();
|
|
26
|
+
return useMemo(
|
|
27
|
+
() => [
|
|
28
|
+
columnHelper.accessor('case_id', {
|
|
29
|
+
id: 'case_id',
|
|
30
|
+
size: 150,
|
|
31
|
+
minSize: 120,
|
|
32
|
+
header: t('caseID'),
|
|
33
|
+
meta: { align: 'left' },
|
|
34
|
+
cell: (info) => (
|
|
35
|
+
<div style={{ width: '100%', minWidth: '100px', textAlign: 'left' }}>
|
|
36
|
+
<PCText size="sm" weight="medium" color="primary">
|
|
37
|
+
{info.getValue()}
|
|
38
|
+
</PCText>
|
|
39
|
+
<PCText
|
|
40
|
+
size="sm"
|
|
41
|
+
color="secondary"
|
|
42
|
+
style={{
|
|
43
|
+
marginTop: pcStyles.mtXs.marginTop,
|
|
44
|
+
display: 'block',
|
|
45
|
+
fontSize: '0.75rem',
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
{formatDate(info.row.original.created_at)}
|
|
49
|
+
</PCText>
|
|
50
|
+
</div>
|
|
51
|
+
),
|
|
52
|
+
enableSorting: false,
|
|
53
|
+
enableColumnFilter: false,
|
|
54
|
+
}),
|
|
55
|
+
columnHelper.accessor('request_type', {
|
|
56
|
+
id: 'request_type',
|
|
57
|
+
size: 150,
|
|
58
|
+
minSize: 120,
|
|
59
|
+
header: t('requestType'),
|
|
60
|
+
meta: { align: 'left' },
|
|
61
|
+
cell: (info) => {
|
|
62
|
+
const row = info.row.original;
|
|
63
|
+
const displayValue = row.request_type_display || info.getValue().replace('_', ' ');
|
|
64
|
+
return (
|
|
65
|
+
<PCText
|
|
66
|
+
size="sm"
|
|
67
|
+
style={{
|
|
68
|
+
textAlign: 'left',
|
|
69
|
+
textTransform: row.request_type_display ? 'none' : 'capitalize',
|
|
70
|
+
width: '100%',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{displayValue}
|
|
74
|
+
</PCText>
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
enableSorting: false,
|
|
78
|
+
enableColumnFilter: false,
|
|
79
|
+
}),
|
|
80
|
+
columnHelper.accessor('status', {
|
|
81
|
+
id: 'status',
|
|
82
|
+
size: 120,
|
|
83
|
+
minSize: 100,
|
|
84
|
+
header: t('status'),
|
|
85
|
+
meta: { align: 'left' },
|
|
86
|
+
cell: (info) => {
|
|
87
|
+
const row = info.row.original;
|
|
88
|
+
const displayStatus = row.status_display || formatCaseStatus(info.getValue());
|
|
89
|
+
return (
|
|
90
|
+
<div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter, ...pcStyles.justifyStart, width: '100%' }}>
|
|
91
|
+
<PCBadge variant={getCaseStatusVariant(info.getValue())} size="sm" className="pc-status-badge">
|
|
92
|
+
{displayStatus}
|
|
93
|
+
</PCBadge>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
enableSorting: false,
|
|
98
|
+
enableColumnFilter: false,
|
|
99
|
+
}),
|
|
100
|
+
columnHelper.accessor('created_at', {
|
|
101
|
+
id: 'created_at',
|
|
102
|
+
size: 180,
|
|
103
|
+
minSize: 140,
|
|
104
|
+
header: t('submittedOn'),
|
|
105
|
+
meta: { align: 'left' },
|
|
106
|
+
cell: (info) => (
|
|
107
|
+
<PCText
|
|
108
|
+
size="sm"
|
|
109
|
+
color="secondary"
|
|
110
|
+
style={{
|
|
111
|
+
textAlign: 'left',
|
|
112
|
+
fontSize: '0.875rem',
|
|
113
|
+
width: '100%',
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
{formatDate(info.getValue())}
|
|
117
|
+
</PCText>
|
|
118
|
+
),
|
|
119
|
+
enableSorting: false,
|
|
120
|
+
enableColumnFilter: false,
|
|
121
|
+
}),
|
|
122
|
+
columnHelper.display({
|
|
123
|
+
id: 'action',
|
|
124
|
+
size: 100,
|
|
125
|
+
minSize: 80,
|
|
126
|
+
header: t('action'),
|
|
127
|
+
meta: { align: 'right' },
|
|
128
|
+
enableSorting: false,
|
|
129
|
+
enableColumnFilter: false,
|
|
130
|
+
cell: () => {
|
|
131
|
+
return (
|
|
132
|
+
<div style={{
|
|
133
|
+
...pcStyles.flex,
|
|
134
|
+
...pcStyles.justifyEnd,
|
|
135
|
+
...pcStyles.gapXs,
|
|
136
|
+
width: '100%',
|
|
137
|
+
}}>
|
|
138
|
+
<PCTooltip content={t('downloadResponse')}>
|
|
139
|
+
<PCActionIcon
|
|
140
|
+
variant="outline"
|
|
141
|
+
size="sm"
|
|
142
|
+
disabled={true}
|
|
143
|
+
style={{
|
|
144
|
+
color: getVar('--pc-text-secondary'),
|
|
145
|
+
opacity: 0.5,
|
|
146
|
+
}}
|
|
147
|
+
aria-label={t('downloadResponse')}
|
|
148
|
+
>
|
|
149
|
+
<FiDownload size={16} />
|
|
150
|
+
</PCActionIcon>
|
|
151
|
+
</PCTooltip>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
],
|
|
157
|
+
[t]
|
|
158
|
+
);
|
|
159
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const GRIEVANCE_TAB_IDS = {
|
|
2
|
+
ALL: 'all',
|
|
3
|
+
PROCESSING: 'processing',
|
|
4
|
+
COMPLETED: 'completed',
|
|
5
|
+
REJECTED: 'rejected',
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
// Re-export shared table configuration
|
|
9
|
+
export {
|
|
10
|
+
PAGE_SIZE_OPTIONS,
|
|
11
|
+
INITIAL_PAGE,
|
|
12
|
+
INITIAL_PAGE_SIZE,
|
|
13
|
+
} from '../config/table';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export const MAX_DISPLAYED_PURPOSE_NAMES_IN_ERASURE_MODAL = 3;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { CaseRequest, UserConsent } from '../lib/types';
|
|
3
|
+
|
|
4
|
+
export type TableColumnAlign = 'left' | 'center' | 'right';
|
|
5
|
+
|
|
6
|
+
export type TableColumnMeta = {
|
|
7
|
+
align?: TableColumnAlign;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type PrivacyCenterLayoutProps = {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
sidebarClassName?: string;
|
|
13
|
+
contentClassName?: string;
|
|
14
|
+
onBack?: 'back' | 'signout';
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CaseHistoryProps = {
|
|
18
|
+
onCreateRequest: () => void;
|
|
19
|
+
isPopup?: boolean;
|
|
20
|
+
onClose?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type CaseDetailsModalProps = {
|
|
24
|
+
isOpen: boolean;
|
|
25
|
+
onClose: () => void;
|
|
26
|
+
selectedCase: CaseRequest | null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type RequestSubmitProps = Readonly<{
|
|
30
|
+
caseId: string;
|
|
31
|
+
contact: string;
|
|
32
|
+
onCreateRequest: () => void;
|
|
33
|
+
revokeConsentOnFulfilment?: boolean;
|
|
34
|
+
revokedPurposeNames?: string;
|
|
35
|
+
}>;
|
|
36
|
+
|
|
37
|
+
export type PaginationProps = {
|
|
38
|
+
currentPage: number;
|
|
39
|
+
totalPages: number;
|
|
40
|
+
totalRecords: number;
|
|
41
|
+
pageSize: number;
|
|
42
|
+
onPageChange: (page: number) => void;
|
|
43
|
+
onPageSizeChange: (size: number) => void;
|
|
44
|
+
className?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type StatusFilterOption = {
|
|
48
|
+
label: string;
|
|
49
|
+
value: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ProductFilterOption = {
|
|
53
|
+
label: string;
|
|
54
|
+
value: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ConsentManagerProps = {
|
|
58
|
+
variant?: 'modern' | 'minimal';
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type ModifyConsentModalProps = {
|
|
62
|
+
isOpen: boolean;
|
|
63
|
+
onClose: () => void;
|
|
64
|
+
selectedRecord: UserConsent | null;
|
|
65
|
+
isLoading: boolean;
|
|
66
|
+
onManageConsent: (
|
|
67
|
+
purposeId: string,
|
|
68
|
+
action: import('../lib/types').ConsentAction,
|
|
69
|
+
nominatorContact?: string,
|
|
70
|
+
dataElementUuids?: string[]
|
|
71
|
+
) => void;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type UseConsentManagerColumnsProps = {
|
|
75
|
+
onOpenModal: (record: UserConsent) => void;
|
|
76
|
+
};
|
|
@@ -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,137 @@
|
|
|
1
|
+
import { createContext, useContext, useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';
|
|
2
|
+
import { setRefreshTokenCookie, decodeJwtToken } from '../lib/utils';
|
|
3
|
+
import type { AuthContextValue } from './types';
|
|
4
|
+
|
|
5
|
+
export type { AuthContextValue } from './types';
|
|
6
|
+
|
|
7
|
+
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
8
|
+
|
|
9
|
+
interface AuthProviderProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
accessToken: string;
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
onError: (error: Error) => boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract and store organization/auth data from JWT token to sessionStorage
|
|
18
|
+
* This makes the SDK self-sufficient - it doesn't depend on host app to set these values
|
|
19
|
+
*/
|
|
20
|
+
const syncJwtDataToSessionStorage = (accessToken: string): void => {
|
|
21
|
+
if (typeof window === 'undefined') return;
|
|
22
|
+
|
|
23
|
+
const decoded = decodeJwtToken(accessToken);
|
|
24
|
+
if (!decoded) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Extract email from JWT (contact or primary_email), fall back to existing sources
|
|
29
|
+
const email = decoded.contact || decoded.primary_email
|
|
30
|
+
|| sessionStorage.getItem('email')
|
|
31
|
+
|| (typeof localStorage !== 'undefined' ? localStorage.getItem('userEmail') : null);
|
|
32
|
+
if (email && typeof email === 'string') {
|
|
33
|
+
sessionStorage.setItem('email', email);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Extract organization UUID
|
|
37
|
+
const orgUuid = decoded.organisation_uuid;
|
|
38
|
+
if (orgUuid && typeof orgUuid === 'string') {
|
|
39
|
+
sessionStorage.setItem('pc_org_id', orgUuid);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Extract workspace UUID
|
|
43
|
+
const workspaceUuid = decoded.workspace_uuid;
|
|
44
|
+
if (workspaceUuid && typeof workspaceUuid === 'string') {
|
|
45
|
+
sessionStorage.setItem('pc_workspace_id', workspaceUuid);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Extract organization slug
|
|
49
|
+
const orgSlug = decoded.organisation_slug;
|
|
50
|
+
if (orgSlug && typeof orgSlug === 'string') {
|
|
51
|
+
sessionStorage.setItem('pc_org_slug', orgSlug);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const AuthProvider = ({
|
|
56
|
+
children,
|
|
57
|
+
accessToken: initialAccessToken,
|
|
58
|
+
refreshToken: initialRefreshToken,
|
|
59
|
+
onError,
|
|
60
|
+
}: AuthProviderProps) => {
|
|
61
|
+
// Sync to sessionStorage synchronously during initialization so child
|
|
62
|
+
// components (ConsentManager, SideBarPC) find the data when they mount.
|
|
63
|
+
// useEffect runs AFTER children render — too late for components that
|
|
64
|
+
// read sessionStorage during their render phase.
|
|
65
|
+
const [accessToken, setAccessToken] = useState(() => {
|
|
66
|
+
if (initialAccessToken && typeof initialAccessToken === 'string' && initialAccessToken.trim() !== '') {
|
|
67
|
+
if (typeof window !== 'undefined') {
|
|
68
|
+
sessionStorage.setItem('access_token', initialAccessToken);
|
|
69
|
+
syncJwtDataToSessionStorage(initialAccessToken);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return initialAccessToken;
|
|
73
|
+
});
|
|
74
|
+
const [refreshToken, setRefreshToken] = useState(() => {
|
|
75
|
+
if (initialRefreshToken && typeof initialRefreshToken === 'string' && initialRefreshToken.trim() !== '') {
|
|
76
|
+
if (typeof window !== 'undefined') {
|
|
77
|
+
setRefreshTokenCookie(initialRefreshToken);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return initialRefreshToken;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Re-sync when props change (for token refresh scenarios)
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (initialAccessToken && typeof initialAccessToken === 'string' && initialAccessToken.trim() !== '') {
|
|
86
|
+
setAccessToken(initialAccessToken);
|
|
87
|
+
if (typeof window !== 'undefined') {
|
|
88
|
+
sessionStorage.setItem('access_token', initialAccessToken);
|
|
89
|
+
syncJwtDataToSessionStorage(initialAccessToken);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}, [initialAccessToken]);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (initialRefreshToken && typeof initialRefreshToken === 'string' && initialRefreshToken.trim() !== '') {
|
|
96
|
+
setRefreshToken(initialRefreshToken);
|
|
97
|
+
if (typeof window !== 'undefined') {
|
|
98
|
+
setRefreshTokenCookie(initialRefreshToken);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, [initialRefreshToken]);
|
|
102
|
+
|
|
103
|
+
// Internal method to update tokens after refresh
|
|
104
|
+
const updateTokens = useCallback((newAccessToken: string, newRefreshToken?: string) => {
|
|
105
|
+
setAccessToken(newAccessToken);
|
|
106
|
+
if (newRefreshToken) {
|
|
107
|
+
setRefreshToken(newRefreshToken);
|
|
108
|
+
}
|
|
109
|
+
// Also update storage
|
|
110
|
+
if (typeof window !== 'undefined') {
|
|
111
|
+
sessionStorage.setItem('access_token', newAccessToken);
|
|
112
|
+
// Extract and store org/auth data from JWT
|
|
113
|
+
syncJwtDataToSessionStorage(newAccessToken);
|
|
114
|
+
if (newRefreshToken) {
|
|
115
|
+
setRefreshTokenCookie(newRefreshToken);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
120
|
+
// Memoize context value to prevent unnecessary re-renders
|
|
121
|
+
const value: AuthContextValue = useMemo(() => ({
|
|
122
|
+
accessToken,
|
|
123
|
+
refreshToken,
|
|
124
|
+
onError,
|
|
125
|
+
updateTokens,
|
|
126
|
+
}), [accessToken, refreshToken, onError, updateTokens]);
|
|
127
|
+
|
|
128
|
+
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const useAuth = (): AuthContextValue => {
|
|
132
|
+
const context = useContext(AuthContext);
|
|
133
|
+
if (!context) {
|
|
134
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
135
|
+
}
|
|
136
|
+
return context;
|
|
137
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, type ReactNode } from 'react';
|
|
2
|
+
import { createConsentClient } from '../api/client';
|
|
3
|
+
import type { BaseUrlContextValue } from './types';
|
|
4
|
+
|
|
5
|
+
export type { BaseUrlContextValue } from './types';
|
|
6
|
+
|
|
7
|
+
export const BaseUrlContext = createContext<BaseUrlContextValue | null>(null);
|
|
8
|
+
|
|
9
|
+
export const BaseUrlProvider = ({
|
|
10
|
+
children,
|
|
11
|
+
baseUrl,
|
|
12
|
+
}: {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
baseUrl: string; // Consent server base URL
|
|
15
|
+
}) => {
|
|
16
|
+
const consentClient = useMemo(() => createConsentClient(baseUrl), [baseUrl]);
|
|
17
|
+
|
|
18
|
+
const value: BaseUrlContextValue = useMemo(
|
|
19
|
+
() => ({
|
|
20
|
+
baseUrl,
|
|
21
|
+
consentClient,
|
|
22
|
+
}),
|
|
23
|
+
[baseUrl, consentClient]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<BaseUrlContext.Provider value={value}>
|
|
28
|
+
{children}
|
|
29
|
+
</BaseUrlContext.Provider>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const useBaseUrl = () => {
|
|
34
|
+
const context = useContext(BaseUrlContext);
|
|
35
|
+
if (!context) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'useBaseUrl must be used within a BaseUrlProvider'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return context;
|
|
41
|
+
};
|