@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.
Files changed (155) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +176 -0
  3. package/dist/index.d.mts +51 -4
  4. package/dist/index.d.ts +51 -4
  5. package/dist/index.js +1912 -345
  6. package/dist/index.mjs +1911 -345
  7. package/dist/privacy-center.d.mts +566 -0
  8. package/dist/privacy-center.d.ts +566 -0
  9. package/dist/privacy-center.js +17174 -0
  10. package/dist/privacy-center.mjs +17136 -0
  11. package/package.json +47 -3
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +201 -118
  13. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1194 -93
  14. package/src/RedactoNoticeConsent/api/index.ts +337 -246
  15. package/src/RedactoNoticeConsent/api/types.ts +140 -41
  16. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +602 -0
  17. package/src/RedactoNoticeConsent/components/index.ts +7 -0
  18. package/src/RedactoNoticeConsent/injectStyles.ts +11 -0
  19. package/src/RedactoNoticeConsent/language-codes.ts +61 -0
  20. package/src/RedactoNoticeConsent/services/storage.ts +122 -0
  21. package/src/RedactoNoticeConsent/types.ts +2 -0
  22. package/src/RedactoNoticeConsentInline/api/index.ts +26 -64
  23. package/src/RedactoNoticeConsentInline/api/types.ts +0 -25
  24. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  25. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  26. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  27. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +27 -0
  28. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +142 -0
  29. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +51 -0
  30. package/src/RedactoPrivacyCenter/api/actions.ts +444 -0
  31. package/src/RedactoPrivacyCenter/api/client.ts +413 -0
  32. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  33. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  34. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  35. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  36. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  37. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  38. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  39. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  40. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  41. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  42. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +468 -0
  44. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +433 -0
  45. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +726 -0
  46. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +239 -0
  47. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  48. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  49. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  50. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  51. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  52. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +305 -0
  53. package/src/RedactoPrivacyCenter/components/Form.tsx +1455 -0
  54. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +176 -0
  55. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +330 -0
  56. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +329 -0
  57. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  58. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +302 -0
  59. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +218 -0
  60. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  61. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +412 -0
  62. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  63. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  64. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  65. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  66. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  67. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  68. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +179 -0
  69. package/src/RedactoPrivacyCenter/components/constant.ts +20 -0
  70. package/src/RedactoPrivacyCenter/components/types.ts +68 -0
  71. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  72. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  73. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  74. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +123 -0
  75. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +44 -0
  76. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  77. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +64 -0
  78. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  79. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  80. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  81. package/src/RedactoPrivacyCenter/lib/api.ts +468 -0
  82. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  83. package/src/RedactoPrivacyCenter/lib/constants.ts +116 -0
  84. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  85. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  86. package/src/RedactoPrivacyCenter/lib/queries.ts +30 -0
  87. package/src/RedactoPrivacyCenter/lib/types.ts +346 -0
  88. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  89. package/src/RedactoPrivacyCenter/lib/utils.ts +625 -0
  90. package/src/RedactoPrivacyCenter/locales/as/translation.json +188 -0
  91. package/src/RedactoPrivacyCenter/locales/bho/translation.json +188 -0
  92. package/src/RedactoPrivacyCenter/locales/bn/translation.json +188 -0
  93. package/src/RedactoPrivacyCenter/locales/brx/translation.json +188 -0
  94. package/src/RedactoPrivacyCenter/locales/doi/translation.json +188 -0
  95. package/src/RedactoPrivacyCenter/locales/en/translation.json +188 -0
  96. package/src/RedactoPrivacyCenter/locales/gom/translation.json +188 -0
  97. package/src/RedactoPrivacyCenter/locales/gu/translation.json +188 -0
  98. package/src/RedactoPrivacyCenter/locales/hi/translation.json +188 -0
  99. package/src/RedactoPrivacyCenter/locales/kn/translation.json +188 -0
  100. package/src/RedactoPrivacyCenter/locales/ks/translation.json +188 -0
  101. package/src/RedactoPrivacyCenter/locales/mai/translation.json +188 -0
  102. package/src/RedactoPrivacyCenter/locales/ml/translation.json +188 -0
  103. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +188 -0
  104. package/src/RedactoPrivacyCenter/locales/mr/translation.json +188 -0
  105. package/src/RedactoPrivacyCenter/locales/ne/translation.json +188 -0
  106. package/src/RedactoPrivacyCenter/locales/or/translation.json +188 -0
  107. package/src/RedactoPrivacyCenter/locales/pa/translation.json +188 -0
  108. package/src/RedactoPrivacyCenter/locales/sa/translation.json +188 -0
  109. package/src/RedactoPrivacyCenter/locales/sat/translation.json +188 -0
  110. package/src/RedactoPrivacyCenter/locales/sd/translation.json +188 -0
  111. package/src/RedactoPrivacyCenter/locales/ta/translation.json +188 -0
  112. package/src/RedactoPrivacyCenter/locales/te/translation.json +188 -0
  113. package/src/RedactoPrivacyCenter/locales/ur/translation.json +188 -0
  114. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  115. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1000 -0
  116. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +819 -0
  117. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  118. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  119. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +365 -0
  120. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +479 -0
  121. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  122. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  123. package/src/RedactoPrivacyCenter/ui/Footer.tsx +73 -0
  124. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  125. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +87 -0
  126. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  127. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  128. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  129. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  130. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  131. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +97 -0
  132. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  133. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +128 -0
  134. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +30 -0
  135. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +104 -0
  136. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +175 -0
  137. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +381 -0
  138. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +137 -0
  139. package/src/RedactoPrivacyCenter/ui/PCText.tsx +39 -0
  140. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  141. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  142. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  143. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  144. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +55 -0
  145. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +70 -0
  146. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  147. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  148. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  149. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  150. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  151. package/src/index.ts +10 -0
  152. package/src/privacy-center.ts +107 -0
  153. package/tsconfig.json +2 -0
  154. package/tsup.config.ts +16 -1
  155. package/src/utils/isLegacyApi.ts +0 -12
@@ -0,0 +1,239 @@
1
+ import { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { PCText, PCTooltip, PCBadge } from '../ui';
4
+ import { cn } from '../utils/cn';
5
+ import { pcStyles } from '../styles/pcStyles';
6
+ import { Smartphone, LaptopMinimal } from 'lucide-react';
7
+ import { createColumnHelper } from '@tanstack/react-table';
8
+ import dayjs from 'dayjs';
9
+ import relativeTime from 'dayjs/plugin/relativeTime';
10
+ import { ConsentStatusEnum } from '../lib/types';
11
+ import type { UserConsent } from '../lib/types';
12
+ import { formatDateTime } from '../lib/consent-manager/utils';
13
+ import type { UseConsentManagerColumnsProps } from './types';
14
+
15
+ dayjs.extend(relativeTime);
16
+
17
+ const columnHelper = createColumnHelper<UserConsent>();
18
+
19
+ export const useConsentManagerColumns = ({
20
+ onOpenModal,
21
+ }: UseConsentManagerColumnsProps) => {
22
+ const { t } = useTranslation();
23
+ return useMemo(
24
+ () => [
25
+ columnHelper.accessor('purpose', {
26
+ id: 'purpose',
27
+ size: 300,
28
+ minSize: 200,
29
+ header: t('purpose'),
30
+ cell: (info) => (
31
+ <div style={{ width: '100%', minWidth: '150px' }}>
32
+ <PCText size="sm" weight="medium">{info.getValue()}</PCText>
33
+ <PCText
34
+ size="sm"
35
+ color="secondary"
36
+ style={{
37
+ display: '-webkit-box',
38
+ WebkitLineClamp: 2,
39
+ WebkitBoxOrient: 'vertical',
40
+ overflow: 'hidden',
41
+ wordBreak: 'break-word',
42
+ fontSize: '0.75rem',
43
+ maxWidth: '400px',
44
+ }}
45
+ title={info.row.original.purpose_description}
46
+ >
47
+ {info.row.original.purpose_description}
48
+ </PCText>
49
+ </div>
50
+ ),
51
+ enableSorting: false,
52
+ enableColumnFilter: false,
53
+ }),
54
+ columnHelper.accessor('product_name', {
55
+ id: 'product',
56
+ size: 150,
57
+ minSize: 100,
58
+ header: t('product'),
59
+ cell: (info) => (
60
+ <div style={{ width: '100%', minWidth: '80px' }}>
61
+ <PCText size="sm" weight="medium">{info.getValue() || '-'}</PCText>
62
+ {info.row.original.product_description && (
63
+ <PCText
64
+ size="sm"
65
+ color="secondary"
66
+ style={{
67
+ display: '-webkit-box',
68
+ WebkitLineClamp: 2,
69
+ WebkitBoxOrient: 'vertical',
70
+ overflow: 'hidden',
71
+ wordBreak: 'break-word',
72
+ fontSize: '0.75rem',
73
+ }}
74
+ title={info.row.original.product_description}
75
+ >
76
+ {info.row.original.product_description}
77
+ </PCText>
78
+ )}
79
+ </div>
80
+ ),
81
+ enableSorting: false,
82
+ enableColumnFilter: false,
83
+ }),
84
+ columnHelper.accessor('status', {
85
+ id: 'status',
86
+ size: 120,
87
+ minSize: 90,
88
+ header: t('status'),
89
+ cell: (info) => {
90
+ const status = info.getValue();
91
+ const getStatusName = (status: string) => {
92
+ if (status === ConsentStatusEnum.ACTIVE) {
93
+ return t('statusActive');
94
+ } else if (status === ConsentStatusEnum.WITHDRAWN) {
95
+ return t('statusWithdrawn');
96
+ } else if (status === ConsentStatusEnum.EXPIRED) {
97
+ return t('statusExpired');
98
+ } else if (status === ConsentStatusEnum.DECLINED) {
99
+ return t('statusDeclined');
100
+ } else {
101
+ return status;
102
+ }
103
+ };
104
+
105
+ const getStatusVariant = (status: string): 'success' | 'error' | 'warning' | 'info' | 'secondary' => {
106
+ if (status === ConsentStatusEnum.ACTIVE) return 'success';
107
+ if (status === ConsentStatusEnum.WITHDRAWN) return 'error';
108
+ if (status === ConsentStatusEnum.EXPIRED) return 'warning';
109
+ if (status === ConsentStatusEnum.DECLINED) return 'warning';
110
+ return 'secondary';
111
+ };
112
+
113
+ return (
114
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter, ...pcStyles.justifyStart, width: '100%' }}>
115
+ <PCBadge variant={getStatusVariant(status)} size="sm" className="pc-status-badge">
116
+ {getStatusName(status)}
117
+ </PCBadge>
118
+ </div>
119
+ );
120
+ },
121
+ enableSorting: false,
122
+ enableColumnFilter: false,
123
+ }),
124
+ columnHelper.accessor('given_date', {
125
+ id: 'given_date',
126
+ size: 180,
127
+ minSize: 140,
128
+ header: t('givenDate'),
129
+ cell: (info) => {
130
+ const timestamp = info.getValue();
131
+ const date = dayjs(timestamp);
132
+ const formattedDateTime = date.format('MMM D, YYYY [at] h:mm A');
133
+ const relativeTimeText = date.fromNow();
134
+
135
+ return (
136
+ <PCTooltip
137
+ content={date.format('MMMM D, YYYY [at] h:mm:ss A')}
138
+ placement="top"
139
+ >
140
+ <div style={{
141
+ ...pcStyles.flex,
142
+ ...pcStyles.flexCol,
143
+ minWidth: '120px',
144
+ gap: '0.125rem',
145
+ }}>
146
+ <PCText size="sm" weight="medium" color="primary">
147
+ {formattedDateTime}
148
+ </PCText>
149
+ <PCText size="sm" color="secondary" style={{ fontSize: '0.75rem' }}>
150
+ {relativeTimeText}
151
+ </PCText>
152
+ </div>
153
+ </PCTooltip>
154
+ );
155
+ },
156
+ enableSorting: false,
157
+ enableColumnFilter: false,
158
+ }),
159
+ columnHelper.accessor('valid_till', {
160
+ id: 'valid_till',
161
+ minSize: 100,
162
+ header: t('validTill'),
163
+ cell: (info) => {
164
+ const status = info.row.original.status;
165
+ if (
166
+ status === ConsentStatusEnum.DECLINED ||
167
+ status === ConsentStatusEnum.WITHDRAWN
168
+ ) {
169
+ return '-';
170
+ }
171
+ const validTillValue = info.getValue();
172
+ if (status === ConsentStatusEnum.ACTIVE && (validTillValue === null || validTillValue === undefined)) {
173
+ return <PCText size="sm">{t('untilWithdrawalNoExpiry')}</PCText>;
174
+ }
175
+ return <PCText size="sm">{formatDateTime(info.getValue())}</PCText>;
176
+ },
177
+ enableSorting: false,
178
+ enableColumnFilter: false,
179
+ }),
180
+ columnHelper.accessor('method', {
181
+ id: 'method',
182
+ size: 120,
183
+ minSize: 80,
184
+ header: t('method'),
185
+ cell: (info) => {
186
+ const method = info.getValue();
187
+ const iconMap: Record<string, JSX.Element> = {
188
+ MOBILE: <Smartphone size={12} style={{ marginRight: '0.25rem', color: '#6b7280' }} />,
189
+ WEB: <LaptopMinimal size={12} style={{ marginRight: '0.25rem', color: '#6b7280' }} />,
190
+ };
191
+ return (
192
+ <div style={{
193
+ ...pcStyles.flex,
194
+ ...pcStyles.itemsCenter,
195
+ minWidth: '60px',
196
+ }}>
197
+ {iconMap[method] || null}
198
+ <PCText size="sm">{method}</PCText>
199
+ </div>
200
+ );
201
+ },
202
+ enableSorting: false,
203
+ enableColumnFilter: false,
204
+ }),
205
+
206
+ columnHelper.display({
207
+ id: 'action',
208
+ size: 100,
209
+ minSize: 70,
210
+ header: t('action'),
211
+ enableSorting: false,
212
+ enableColumnFilter: false,
213
+ cell: ({ row }) => {
214
+ const isDisabled =
215
+ row.original.status === ConsentStatusEnum.DECLINED;
216
+
217
+ return (
218
+ <button
219
+ style={{
220
+ fontSize: pcStyles.textSm.fontSize,
221
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
222
+ cursor: isDisabled ? 'not-allowed' : 'pointer',
223
+ color: isDisabled ? '#9ca3af' : '#4F87FF',
224
+ background: 'none',
225
+ border: 'none',
226
+ padding: 0,
227
+ }}
228
+ onClick={() => onOpenModal(row.original)}
229
+ disabled={isDisabled}
230
+ >
231
+ {t('modifyButton')}
232
+ </button>
233
+ );
234
+ },
235
+ }),
236
+ ],
237
+ [onOpenModal, t]
238
+ );
239
+ };
@@ -0,0 +1,7 @@
1
+ // Re-export shared table configuration
2
+ export {
3
+ SEARCH_DEBOUNCE_DELAY,
4
+ DEFAULT_PAGE,
5
+ DEFAULT_PAGE_SIZE,
6
+ SKELETON_ROW_COUNT,
7
+ } from '../config/table';
@@ -0,0 +1,155 @@
1
+ import { SKELETON_ROW_COUNT } from './ConsentManagerConstants';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ const getVar = (varName: string): string => `var(${varName})`;
5
+
6
+ export const ConsentManagerTableSkeleton = () => {
7
+ return (
8
+ <div style={{
9
+ borderRadius: getVar('--pc-border-radius-md'),
10
+ border: `1px solid ${getVar('--pc-border-color')}`,
11
+ backgroundColor: getVar('--pc-bg-primary'),
12
+ }}>
13
+ {/* Table Header */}
14
+ <div style={{
15
+ borderBottom: `1px solid ${getVar('--pc-border-color')}`,
16
+ backgroundColor: getVar('--pc-bg-secondary'),
17
+ }}>
18
+ <div style={{
19
+ display: 'grid',
20
+ gridTemplateColumns: 'repeat(7, 1fr)',
21
+ gap: getVar('--pc-spacing-md'),
22
+ paddingLeft: getVar('--pc-spacing-lg'),
23
+ paddingRight: getVar('--pc-spacing-lg'),
24
+ paddingTop: getVar('--pc-spacing-md'),
25
+ paddingBottom: getVar('--pc-spacing-md'),
26
+ }} className="pc-grid-responsive">
27
+ {Array.from({ length: 7 }).map((_, i) => (
28
+ <div
29
+ key={i}
30
+ style={{
31
+ height: '1rem',
32
+ width: '6rem',
33
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
34
+ borderRadius: getVar('--pc-border-radius-md'),
35
+ backgroundColor: getVar('--pc-bg-tertiary'),
36
+ }}
37
+ />
38
+ ))}
39
+ </div>
40
+ </div>
41
+
42
+ {/* Table Rows */}
43
+ {Array.from({ length: SKELETON_ROW_COUNT }).map((_, index) => (
44
+ <div
45
+ key={index}
46
+ style={{
47
+ borderBottom: `1px solid ${getVar('--pc-border-color')}`,
48
+ backgroundColor: getVar('--pc-bg-primary'),
49
+ }}
50
+ >
51
+ <div style={{
52
+ display: 'grid',
53
+ gridTemplateColumns: 'repeat(7, 1fr)',
54
+ gap: getVar('--pc-spacing-md'),
55
+ paddingLeft: getVar('--pc-spacing-lg'),
56
+ paddingRight: getVar('--pc-spacing-lg'),
57
+ paddingTop: getVar('--pc-spacing-md'),
58
+ paddingBottom: getVar('--pc-spacing-md'),
59
+ }} className="pc-grid-responsive">
60
+ {/* Purpose */}
61
+ <div style={{ ...pcStyles.flex, ...pcStyles.flexCol, ...pcStyles.gapXs }}>
62
+ <div style={{
63
+ height: '1rem',
64
+ width: '8rem',
65
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
66
+ borderRadius: getVar('--pc-border-radius-md'),
67
+ backgroundColor: getVar('--pc-bg-tertiary'),
68
+ }} />
69
+ <div style={{
70
+ height: '0.75rem',
71
+ width: '12rem',
72
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
73
+ borderRadius: getVar('--pc-border-radius-md'),
74
+ backgroundColor: getVar('--pc-bg-tertiary'),
75
+ }} />
76
+ </div>
77
+
78
+ {/* Product */}
79
+ <div style={{ ...pcStyles.flex, ...pcStyles.flexCol, ...pcStyles.gapXs }}>
80
+ <div style={{
81
+ height: '1rem',
82
+ width: '6rem',
83
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
84
+ borderRadius: getVar('--pc-border-radius-md'),
85
+ backgroundColor: getVar('--pc-bg-tertiary'),
86
+ }} />
87
+ <div style={{
88
+ height: '0.75rem',
89
+ width: '8rem',
90
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
91
+ borderRadius: getVar('--pc-border-radius-md'),
92
+ backgroundColor: getVar('--pc-bg-tertiary'),
93
+ }} />
94
+ </div>
95
+
96
+ {/* Status */}
97
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter }}>
98
+ <div style={{
99
+ height: '1.5rem',
100
+ width: '5rem',
101
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
102
+ borderRadius: '9999px',
103
+ backgroundColor: getVar('--pc-bg-tertiary'),
104
+ }} />
105
+ </div>
106
+
107
+ {/* Given Date */}
108
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter }}>
109
+ <div style={{
110
+ height: '1rem',
111
+ width: '6rem',
112
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
113
+ borderRadius: getVar('--pc-border-radius-md'),
114
+ backgroundColor: getVar('--pc-bg-tertiary'),
115
+ }} />
116
+ </div>
117
+
118
+ {/* Valid till */}
119
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter }}>
120
+ <div style={{
121
+ height: '1rem',
122
+ width: '6rem',
123
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
124
+ borderRadius: getVar('--pc-border-radius-md'),
125
+ backgroundColor: getVar('--pc-bg-tertiary'),
126
+ }} />
127
+ </div>
128
+
129
+ {/* Method */}
130
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter }}>
131
+ <div style={{
132
+ height: '1rem',
133
+ width: '4rem',
134
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
135
+ borderRadius: getVar('--pc-border-radius-md'),
136
+ backgroundColor: getVar('--pc-bg-tertiary'),
137
+ }} />
138
+ </div>
139
+
140
+ {/* Action */}
141
+ <div style={{ ...pcStyles.flex, ...pcStyles.itemsCenter }}>
142
+ <div style={{
143
+ height: '1.5rem',
144
+ width: '4rem',
145
+ animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
146
+ borderRadius: getVar('--pc-border-radius-md'),
147
+ backgroundColor: getVar('--pc-bg-tertiary'),
148
+ }} />
149
+ </div>
150
+ </div>
151
+ </div>
152
+ ))}
153
+ </div>
154
+ );
155
+ };
@@ -0,0 +1,189 @@
1
+ import { PCCheckbox, PCInput } from '../../ui';
2
+ import { pcStyles } from '../../styles/pcStyles';
3
+ import type { DataElement } from '../../lib/types';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ const getVar = (varName: string): string => `var(${varName})`;
7
+
8
+ export default function CorrectionDataSection({
9
+ dataElements,
10
+ selected,
11
+ setSelected,
12
+ correctionValues,
13
+ setCorrectionValues,
14
+ isTranslating = false,
15
+ }: {
16
+ dataElements: DataElement[];
17
+ selected: string[];
18
+ setSelected: (values: string[]) => void;
19
+ correctionValues: Record<string, { current: string; updated: string }>;
20
+ setCorrectionValues: (
21
+ values: Record<string, { current: string; updated: string }>
22
+ ) => void;
23
+ isTranslating?: boolean;
24
+ }) {
25
+ const { t } = useTranslation();
26
+
27
+ const handleSelectAll = (checked: boolean) => {
28
+ setSelected(checked ? dataElements.map((el) => el.name) : []);
29
+ };
30
+
31
+ const handleToggle = (name: string, checked: boolean) => {
32
+ setSelected(
33
+ checked ? [...selected, name] : selected.filter((n) => n !== name)
34
+ );
35
+ };
36
+
37
+ const getElementByName = (name: string) =>
38
+ dataElements.find((el) => el.name === name);
39
+
40
+ if (isTranslating) {
41
+ return (
42
+ <div>
43
+ <div style={{ marginBottom: '0.375rem', paddingLeft: pcStyles.pxMd.paddingLeft, paddingRight: pcStyles.pxMd.paddingRight }}>
44
+ <div style={{ height: '1rem', width: '10rem', animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', borderRadius: pcStyles.borderRadiusMd, backgroundColor: getVar('--pc-bg-tertiary'), marginBottom: '0.375rem' }}></div>
45
+ <div style={{ height: '1rem', width: '6rem', animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', borderRadius: pcStyles.borderRadiusMd, backgroundColor: getVar('--pc-bg-tertiary'), marginBottom: '0.5rem' }}></div>
46
+ </div>
47
+ <div style={{
48
+ backgroundColor: getVar('--pc-bg-secondary'),
49
+ borderRadius: pcStyles.borderRadiusMd,
50
+ padding: getVar('--pc-spacing-sm'),
51
+ border: `1px solid ${getVar('--pc-border-color')}`,
52
+ }}>
53
+ {[1, 2, 3, 4].map((skeletonIndex) => (
54
+ <div key={skeletonIndex} style={{ marginBottom: skeletonIndex < 4 ? getVar('--pc-spacing-xs') : 0, ...pcStyles.flex, ...pcStyles.itemsCenter, ...pcStyles.gapSm }}>
55
+ <div style={{ height: '1rem', width: '1rem', animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', borderRadius: pcStyles.borderRadiusMd, backgroundColor: getVar('--pc-bg-tertiary') }}></div>
56
+ <div style={{ height: '1rem', width: '8rem', animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', borderRadius: pcStyles.borderRadiusMd, backgroundColor: getVar('--pc-bg-tertiary') }}></div>
57
+ </div>
58
+ ))}
59
+ </div>
60
+ </div>
61
+ );
62
+ }
63
+
64
+ return (
65
+ <>
66
+ <div style={{ ...pcStyles.flex, ...pcStyles.flexCol, gap: getVar('--pc-spacing-xs') }}>
67
+ <p style={{
68
+ margin: 0,
69
+ fontSize: pcStyles.textXs.fontSize,
70
+ color: getVar('--pc-text-secondary'),
71
+ }}>
72
+ {t('selectFieldsToUpdate')}
73
+ </p>
74
+ <div style={{
75
+ backgroundColor: getVar('--pc-bg-secondary'),
76
+ borderRadius: pcStyles.borderRadiusMd,
77
+ padding: getVar('--pc-spacing-sm'),
78
+ border: `1px solid ${getVar('--pc-border-color')}`,
79
+ }}>
80
+ <PCCheckbox
81
+ label={t('selectAll')}
82
+ size="sm"
83
+ checked={
84
+ dataElements.length > 0 && selected.length === dataElements.length
85
+ }
86
+ onChange={(e) => handleSelectAll(e.target.checked)}
87
+ labelClassName="text-sm font-medium"
88
+ style={{ marginBottom: getVar('--pc-spacing-xl') }}
89
+ />
90
+ <div style={{
91
+ display: 'grid',
92
+ gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
93
+ gap: getVar('--pc-spacing-md'),
94
+ }}>
95
+ {dataElements.map((element) => (
96
+ <PCCheckbox
97
+ key={element.uuid}
98
+ label={element.name}
99
+ size="sm"
100
+ checked={selected.includes(element.name)}
101
+ onChange={(e) => handleToggle(element.name, e.target.checked)}
102
+ labelClassName="text-sm capitalize"
103
+ />
104
+ ))}
105
+ </div>
106
+ </div>
107
+ </div>
108
+ {selected.length > 0 && (
109
+ <div style={{
110
+ marginTop: getVar('--pc-spacing-md'),
111
+ paddingTop: getVar('--pc-spacing-md'),
112
+ borderTop: `1px solid ${getVar('--pc-border-color')}`,
113
+ }}>
114
+ <p style={{
115
+ marginBottom: getVar('--pc-spacing-sm'),
116
+ fontSize: pcStyles.textSm.fontSize,
117
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
118
+ color: getVar('--pc-text-primary'),
119
+ }}>
120
+ <span style={{ color: getVar('--pc-error') }}>*</span>{' '}
121
+ {t('enterCorrectValue')}
122
+ </p>
123
+ <div style={{
124
+ display: 'grid',
125
+ gridTemplateColumns: 'repeat(1, 1fr)',
126
+ gap: getVar('--pc-spacing-sm'),
127
+ }}>
128
+ {selected.map((name) => {
129
+ const field = getElementByName(name);
130
+ return (
131
+ <div key={name} style={{
132
+ padding: getVar('--pc-spacing-sm'),
133
+ backgroundColor: getVar('--pc-bg-secondary'),
134
+ borderRadius: pcStyles.borderRadiusSm,
135
+ }}>
136
+ <label style={{
137
+ marginBottom: getVar('--pc-spacing-xs'),
138
+ display: 'block',
139
+ fontSize: pcStyles.textSm.fontSize,
140
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
141
+ color: getVar('--pc-text-primary'),
142
+ }}>
143
+ {field?.name}
144
+ </label>
145
+ <div style={{
146
+ display: 'grid',
147
+ gridTemplateColumns: 'repeat(1, 1fr)',
148
+ gap: getVar('--pc-spacing-xs'),
149
+ }}>
150
+ <PCInput
151
+ placeholder="Current value"
152
+ size="sm"
153
+ required
154
+ value={correctionValues[name]?.current || ''}
155
+ onChange={(e) =>
156
+ setCorrectionValues({
157
+ ...correctionValues,
158
+ [name]: {
159
+ ...correctionValues[name],
160
+ current: e.target.value,
161
+ },
162
+ })
163
+ }
164
+ />
165
+ <PCInput
166
+ placeholder="Updated value"
167
+ size="sm"
168
+ required
169
+ value={correctionValues[name]?.updated || ''}
170
+ onChange={(e) =>
171
+ setCorrectionValues({
172
+ ...correctionValues,
173
+ [name]: {
174
+ ...correctionValues[name],
175
+ updated: e.target.value,
176
+ },
177
+ })
178
+ }
179
+ />
180
+ </div>
181
+ </div>
182
+ );
183
+ })}
184
+ </div>
185
+ </div>
186
+ )}
187
+ </>
188
+ );
189
+ }