@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,412 @@
1
+ import { useState } from 'react';
2
+ import { PCTitle, PCCollapse, PCTooltip } from '../../ui';
3
+ import { pcStyles } from '../../styles/pcStyles';
4
+ import { cn } from '../../utils/cn';
5
+ import { PiCaretDownBold } from 'react-icons/pi';
6
+ import { MdLineWeight } from 'react-icons/md';
7
+
8
+ const getVar = (varName: string): string => `var(${varName})`;
9
+ import StatusBadge from '../../ui/StatusBadge';
10
+ import { useInternalNavigation, type InternalPage } from '../../context/NavigationContext';
11
+ import { useTranslation } from 'react-i18next';
12
+ import type { BadgeType, MenuBadgeProps, SideMenuPCProps } from './types';
13
+ import { badges } from './constants';
14
+
15
+ export type { DropdownItem, MenuItem, SideMenuPCProps } from './types';
16
+
17
+ const MenuBadge = ({ type, isDisabled }: MenuBadgeProps) => {
18
+ const { t } = useTranslation();
19
+ const badge = badges[type];
20
+ if (!badge || isDisabled) return null;
21
+
22
+ const isNewBadge = type === 'new';
23
+ const displayText = isNewBadge ? t('badgeNew') : badge.text;
24
+
25
+ return (
26
+ <span
27
+ style={{
28
+ marginLeft: pcStyles.mSm.marginLeft,
29
+ borderRadius: '9999px',
30
+ paddingLeft: '0.5rem',
31
+ paddingRight: '0.5rem',
32
+ paddingTop: '0.125rem',
33
+ paddingBottom: '0.125rem',
34
+ fontSize: pcStyles.textXs.fontSize,
35
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
36
+ ...(isNewBadge ? {
37
+ backgroundColor: getVar('--pc-bg-hover'),
38
+ color: getVar('--pc-primary'),
39
+ } : {
40
+ backgroundColor: getVar('--pc-bg-tertiary'),
41
+ color: getVar('--pc-text-secondary'),
42
+ }),
43
+ }}
44
+ >
45
+ {displayText}
46
+ </span>
47
+ );
48
+ };
49
+
50
+ export function SideMenuPC({ menuItems, isCollapsed, onItemSelect }: SideMenuPCProps) {
51
+ const { currentPage, navigateTo } = useInternalNavigation();
52
+
53
+ return (
54
+ <div
55
+ className="pc-sidebar-menu-container"
56
+ style={{
57
+ display: 'flex',
58
+ flexDirection: 'column',
59
+ paddingTop: '0.625rem',
60
+ paddingBottom: '0.625rem',
61
+ paddingLeft: '0.5rem',
62
+ paddingRight: '0.5rem',
63
+ width: '100%',
64
+ }}
65
+ >
66
+ {menuItems.map((item, index) => {
67
+ // Check if current page matches this item's href
68
+ const isActive = item?.href ? currentPage === (item.href as InternalPage) : false;
69
+ const pathnameExistInDropdowns = item?.dropdownItems?.filter(
70
+ (dropdownItem) => {
71
+ return currentPage === (dropdownItem.href as InternalPage);
72
+ }
73
+ );
74
+ const isDropdownOpen = Boolean(pathnameExistInDropdowns?.length);
75
+
76
+ // Handler for navigation
77
+ const handleNavigate = (href: string) => {
78
+ if (!item.isComingSoon && href) {
79
+ navigateTo(href as InternalPage);
80
+ onItemSelect?.();
81
+ }
82
+ };
83
+
84
+ const renderItemWithTooltip = (content: React.ReactNode) => {
85
+ if (isCollapsed) {
86
+ return (
87
+ <PCTooltip content={item.name} placement="right" color="invert">
88
+ <div style={{ width: 'fit-content', margin: '0 auto' }}>{content}</div>
89
+ </PCTooltip>
90
+ );
91
+ }
92
+ return content;
93
+ };
94
+
95
+ return (
96
+ <div
97
+ key={item.name + '-' + index}
98
+ style={{
99
+ width: '100%',
100
+ display: 'block',
101
+ marginBottom: isCollapsed ? '0.875rem' : '0.125rem',
102
+ }}
103
+ >
104
+ {item?.href ? (
105
+ <>
106
+ {item?.dropdownItems ? (
107
+ <PCCollapse
108
+ defaultOpen={isDropdownOpen}
109
+ >
110
+ {({ open, toggle }) => (
111
+ <>
112
+ <PCCollapse.Header
113
+ open={open}
114
+ toggle={toggle}
115
+ >
116
+ {renderItemWithTooltip(
117
+ <div
118
+ onClick={!item.isComingSoon ? toggle : undefined}
119
+ style={{
120
+ position: 'relative',
121
+ marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
122
+ marginBottom: isCollapsed ? 0 : '0.1875rem',
123
+ ...pcStyles.flex,
124
+ ...pcStyles.itemsCenter,
125
+ ...pcStyles.justifyCenter,
126
+ ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
127
+ borderRadius: pcStyles.borderRadiusMd,
128
+ padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
129
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
130
+ cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
131
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
132
+ backgroundColor: isDropdownOpen && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
133
+ opacity: item.isComingSoon ? 0.5 : 1,
134
+ transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
135
+ minHeight: isCollapsed ? '2rem' : '2.25rem',
136
+ width: isCollapsed ? 'fit-content' : '100%',
137
+ willChange: 'padding, justify-content',
138
+ }}
139
+ >
140
+ <div
141
+ style={{
142
+ ...pcStyles.flex,
143
+ ...pcStyles.itemsCenter,
144
+ ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
145
+ pointerEvents: item.isComingSoon ? 'none' : 'auto',
146
+ transition: 'justify-content 0.3s ease, width 0.3s ease',
147
+ willChange: 'justify-content, width',
148
+ }}
149
+ >
150
+ {item?.icon && (
151
+ <span
152
+ style={{
153
+ marginRight: isCollapsed ? 0 : pcStyles.mSm.marginRight,
154
+ display: 'inline-flex',
155
+ height: '1.25rem',
156
+ width: '1.25rem',
157
+ ...pcStyles.itemsCenter,
158
+ ...pcStyles.justifyCenter,
159
+ borderRadius: pcStyles.borderRadiusMd,
160
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
161
+ transition: 'margin-right 0.3s ease',
162
+ willChange: 'margin-right',
163
+ }}
164
+ >
165
+ {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
166
+ </span>
167
+ )}
168
+ <span style={{
169
+ overflow: 'hidden',
170
+ textOverflow: 'ellipsis',
171
+ whiteSpace: 'nowrap',
172
+ marginLeft: isCollapsed ? 0 : '0.5rem',
173
+ opacity: isCollapsed ? 0 : 1,
174
+ width: isCollapsed ? 0 : 'auto',
175
+ maxWidth: isCollapsed ? 0 : '100%',
176
+ transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
177
+ willChange: 'opacity, width, max-width, margin-left',
178
+ }}>
179
+ {item.name}
180
+ {item.isNew && (
181
+ <MenuBadge
182
+ type="new"
183
+ isDisabled={item.isComingSoon}
184
+ />
185
+ )}
186
+ </span>
187
+ </div>
188
+
189
+ <div style={{
190
+ opacity: (!item.isComingSoon && !isCollapsed) ? 1 : 0,
191
+ width: (!item.isComingSoon && !isCollapsed) ? 'auto' : 0,
192
+ overflow: 'hidden',
193
+ transition: 'opacity 0.3s ease, width 0.3s ease',
194
+ willChange: 'opacity, width',
195
+ flexShrink: 0,
196
+ }}>
197
+ {!item.isComingSoon && (
198
+ <PiCaretDownBold
199
+ strokeWidth={3}
200
+ size={14}
201
+ style={{
202
+ transform: open ? 'rotate(0deg)' : 'rotate(-90deg)',
203
+ transition: 'transform 0.2s ease',
204
+ color: getVar('--pc-text-secondary'),
205
+ }}
206
+ />
207
+ )}
208
+ </div>
209
+ </div>
210
+ )}
211
+ </PCCollapse.Header>
212
+ <PCCollapse.Body open={open}>
213
+ {!isCollapsed &&
214
+ item?.dropdownItems?.map((dropdownItem, index) => {
215
+ const isChildActive = currentPage === (dropdownItem.href as InternalPage);
216
+
217
+ return renderItemWithTooltip(
218
+ <div
219
+ key={dropdownItem?.name + index}
220
+ onClick={() => handleNavigate(dropdownItem.href)}
221
+ style={{
222
+ marginLeft: '1.25rem',
223
+ marginRight: '0.625rem',
224
+ marginTop: index === 0 ? '0.1875rem' : '0.125rem',
225
+ marginBottom: '0.125rem',
226
+ ...pcStyles.flex,
227
+ ...pcStyles.itemsCenter,
228
+ borderRadius: pcStyles.borderRadiusMd,
229
+ padding: '0.4375rem 0.75rem',
230
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
231
+ textTransform: 'capitalize',
232
+ cursor: dropdownItem.isComingSoon ? 'not-allowed' : 'pointer',
233
+ color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-secondary'),
234
+ backgroundColor: isChildActive && !dropdownItem.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
235
+ opacity: dropdownItem.isComingSoon ? 0.5 : 1,
236
+ transition: 'color 0.2s, background-color 0.2s',
237
+ minHeight: '2rem',
238
+ }}
239
+ >
240
+ <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter }}>
241
+ <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
242
+ {dropdownItem?.icon && (
243
+ <span
244
+ style={{
245
+ marginRight: pcStyles.mSm.marginRight,
246
+ display: 'inline-flex',
247
+ height: '1.125rem',
248
+ width: '1.125rem',
249
+ ...pcStyles.itemsCenter,
250
+ ...pcStyles.justifyCenter,
251
+ borderRadius: pcStyles.borderRadiusMd,
252
+ color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
253
+ }}
254
+ >
255
+ {dropdownItem.icon}
256
+ </span>
257
+ )}
258
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
259
+ {dropdownItem?.name}
260
+ </span>
261
+ {dropdownItem.isNew && (
262
+ <MenuBadge
263
+ type="new"
264
+ isDisabled={dropdownItem.isComingSoon}
265
+ />
266
+ )}
267
+ </div>
268
+ <div style={{ marginLeft: pcStyles.mMd.marginLeft, ...pcStyles.flex, ...pcStyles.itemsCenter }}>
269
+ {dropdownItem?.badge?.length ? (
270
+ <StatusBadge status={dropdownItem?.badge} className="pc-status-badge" />
271
+ ) : (
272
+ <div style={{ width: '1rem' }} />
273
+ )}
274
+ </div>
275
+ </div>
276
+ </div>
277
+ );
278
+ })}
279
+ </PCCollapse.Body>
280
+ </>
281
+ )}
282
+ </PCCollapse>
283
+ ) : (
284
+ renderItemWithTooltip(
285
+ <div
286
+ key={item.name + '-' + index}
287
+ onClick={() => handleNavigate(item.href)}
288
+ style={{
289
+ position: 'relative',
290
+ marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
291
+ marginBottom: isCollapsed ? 0 : '0.1875rem',
292
+ ...pcStyles.flex,
293
+ ...pcStyles.itemsCenter,
294
+ ...pcStyles.justifyCenter,
295
+ ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
296
+ borderRadius: pcStyles.borderRadiusMd,
297
+ padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
298
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
299
+ textTransform: 'capitalize',
300
+ cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
301
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
302
+ backgroundColor: isActive && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
303
+ opacity: item.isComingSoon ? 0.5 : 1,
304
+ transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
305
+ minHeight: isCollapsed ? '2rem' : '2.25rem',
306
+ width: isCollapsed ? 'fit-content' : '100%',
307
+ willChange: 'padding, justify-content',
308
+ }}
309
+ >
310
+ <div style={{
311
+ ...pcStyles.flex,
312
+ ...pcStyles.itemsCenter,
313
+ ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
314
+ transition: 'justify-content 0.3s ease, width 0.3s ease',
315
+ willChange: 'justify-content, width',
316
+ }}>
317
+ {item?.icon && (
318
+ <span
319
+ style={{
320
+ marginRight: isCollapsed ? 0 : pcStyles.mSm.marginRight,
321
+ display: 'inline-flex',
322
+ height: '1.25rem',
323
+ width: '1.25rem',
324
+ ...pcStyles.itemsCenter,
325
+ ...pcStyles.justifyCenter,
326
+ borderRadius: pcStyles.borderRadiusMd,
327
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
328
+ transition: 'margin-right 0.3s ease',
329
+ willChange: 'margin-right',
330
+ }}
331
+ >
332
+ {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
333
+ </span>
334
+ )}
335
+ <span style={{
336
+ overflow: 'hidden',
337
+ textOverflow: 'ellipsis',
338
+ whiteSpace: 'nowrap',
339
+ marginLeft: isCollapsed ? 0 : '0.5rem',
340
+ opacity: isCollapsed ? 0 : 1,
341
+ width: isCollapsed ? 0 : 'auto',
342
+ maxWidth: isCollapsed ? 0 : '100%',
343
+ transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
344
+ willChange: 'opacity, width, max-width, margin-left',
345
+ }}>
346
+ {item.name}
347
+ {item.isNew && <MenuBadge type="new" />}
348
+ </span>
349
+ </div>
350
+ <div style={{
351
+ opacity: (!isCollapsed && item?.badge?.length) ? 1 : 0,
352
+ width: (!isCollapsed && item?.badge?.length) ? 'auto' : 0,
353
+ overflow: 'hidden',
354
+ transition: 'opacity 0.3s ease, width 0.3s ease',
355
+ willChange: 'opacity, width',
356
+ flexShrink: 0,
357
+ }}>
358
+ {item?.badge?.length ? <StatusBadge status={item?.badge} className="pc-status-badge" /> : null}
359
+ </div>
360
+ </div>
361
+ )
362
+ )}
363
+ </>
364
+ ) : (
365
+ <>
366
+ {isCollapsed ? (
367
+ <div
368
+ style={{
369
+ marginLeft: '0.75rem',
370
+ marginRight: '0.75rem',
371
+ marginTop: index !== 0 ? '1rem' : '0.5rem',
372
+ marginBottom: '0.5rem',
373
+ height: '1px',
374
+ }}
375
+ >
376
+ <hr style={{
377
+ width: '100%',
378
+ backgroundColor: getVar('--pc-border-color'),
379
+ border: 'none',
380
+ height: '1px',
381
+ margin: 0,
382
+ }} />
383
+ </div>
384
+ ) : (
385
+ <PCTitle
386
+ as="h6"
387
+ style={{
388
+ marginTop: index !== 0 ? '1.25rem' : '0.5rem',
389
+ marginBottom: '0.375rem',
390
+ overflow: 'hidden',
391
+ textOverflow: 'ellipsis',
392
+ whiteSpace: 'nowrap',
393
+ paddingLeft: '1rem',
394
+ paddingRight: '1rem',
395
+ fontSize: pcStyles.textXs.fontSize,
396
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
397
+ textTransform: 'uppercase',
398
+ letterSpacing: '0.1em',
399
+ color: getVar('--pc-text-secondary'),
400
+ }}
401
+ >
402
+ {item.name}
403
+ </PCTitle>
404
+ )}
405
+ </>
406
+ )}
407
+ </div>
408
+ );
409
+ })}
410
+ </div>
411
+ );
412
+ }
@@ -0,0 +1,42 @@
1
+ import {
2
+ IoAlertCircleOutline,
3
+ IoPulseOutline,
4
+ IoShieldCheckmarkOutline,
5
+ } from 'react-icons/io5';
6
+ import type { BadgeType, MenuItem } from './types';
7
+
8
+ export const menuItemsPC: MenuItem[] = [
9
+ {
10
+ name: 'Consent Manager',
11
+ href: 'consent-manager',
12
+ icon: <IoShieldCheckmarkOutline size={20} />,
13
+ },
14
+ {
15
+ name: 'Grievance',
16
+ href: 'form',
17
+ icon: <IoAlertCircleOutline size={20} />,
18
+ },
19
+ {
20
+ name: 'Activity',
21
+ href: 'activity',
22
+ icon: <IoPulseOutline size={20} />,
23
+ },
24
+ ];
25
+
26
+ export const badges: Record<BadgeType, { text: string; className: string }> = {
27
+ new: {
28
+ text: 'New',
29
+ className: 'bg-blue-100 text-blue-500',
30
+ },
31
+ coming: {
32
+ text: '',
33
+ className: 'visibility-hidden',
34
+ },
35
+ };
36
+
37
+ export const LOCAL_STORAGE_KEYS_TO_REMOVE = [
38
+ 'email',
39
+ 'access_token',
40
+ 'i18nextLng',
41
+ 'lang',
42
+ ] as const;
@@ -0,0 +1 @@
1
+ export { menuItemsPC } from './constants';
@@ -0,0 +1,40 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ export type BadgeType = 'new' | 'coming';
4
+
5
+ export type MenuBadgeProps = {
6
+ type: BadgeType;
7
+ isDisabled?: boolean;
8
+ };
9
+
10
+ export type DropdownItem = {
11
+ name: string;
12
+ href: string;
13
+ badge?: string;
14
+ isNew?: boolean;
15
+ isComingSoon?: boolean;
16
+ icon?: ReactElement;
17
+ };
18
+
19
+ export type MenuItem = {
20
+ name: string;
21
+ href?: string;
22
+ icon?: ReactElement;
23
+ isBatch?: boolean;
24
+ isNew?: boolean;
25
+ isComingSoon?: boolean;
26
+ badge?: string;
27
+ dropdownItems?: DropdownItem[];
28
+ };
29
+
30
+ export type SideMenuPCProps = {
31
+ menuItems: MenuItem[];
32
+ isCollapsed: boolean;
33
+ onItemSelect?: () => void;
34
+ };
35
+
36
+ export type SideBarPCProps = {
37
+ className?: string;
38
+ isCompact?: boolean;
39
+ onBack?: 'back' | 'signout';
40
+ };
@@ -0,0 +1,38 @@
1
+ import { LOCAL_STORAGE_KEYS_TO_REMOVE } from './constants';
2
+
3
+ export const clearLocalStorage = () => {
4
+ try {
5
+ LOCAL_STORAGE_KEYS_TO_REMOVE.forEach((key) => {
6
+ sessionStorage.removeItem(key);
7
+ });
8
+ } catch {
9
+ // Silently ignore if sessionStorage is unavailable
10
+ }
11
+ };
12
+
13
+ // Alias for clarity - both functions clear sessionStorage
14
+ export const clearSessionStorage = clearLocalStorage;
15
+
16
+ export const generateOrgSlug = (
17
+ slug: string | undefined,
18
+ name: string | undefined
19
+ ): string | undefined => {
20
+ if (slug) return slug;
21
+ if (!name) return undefined;
22
+
23
+ const normalizedName = name.toLowerCase();
24
+ const parts = normalizedName.split(' ').filter((part) => part.length > 0);
25
+ return parts.length > 0 ? parts.join('-') : undefined;
26
+ };
27
+
28
+ /**
29
+ * Convert a slug to a readable organization name
30
+ * e.g., "my-org-name" -> "My Org Name"
31
+ */
32
+ export const slugToOrgName = (slug: string): string => {
33
+ if (!slug) return '';
34
+ return slug
35
+ .split('-')
36
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
37
+ .join(' ');
38
+ };
@@ -0,0 +1,85 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { IoSunnyOutline, IoMoonOutline } from 'react-icons/io5';
3
+ import { useTheme } from '../context/theme';
4
+
5
+ export default function ThemeTransitionOverlay() {
6
+ const { isTransitioning, targetTheme } = useTheme();
7
+ const overlayRef = useRef<HTMLDivElement>(null);
8
+ const iconRef = useRef<HTMLDivElement>(null);
9
+
10
+ useEffect(() => {
11
+ if (isTransitioning && overlayRef.current && iconRef.current) {
12
+ const overlay = overlayRef.current;
13
+ const icon = iconRef.current;
14
+
15
+ // Trigger animation
16
+ overlay.style.display = 'flex';
17
+ // Force reflow to ensure display change is applied
18
+ overlay.offsetHeight;
19
+ overlay.style.opacity = '1';
20
+ icon.style.animation = 'none';
21
+ // Force reflow
22
+ icon.offsetHeight;
23
+ icon.style.animation = '';
24
+
25
+ const timeout = setTimeout(() => {
26
+ if (overlay) {
27
+ overlay.style.opacity = '0';
28
+ setTimeout(() => {
29
+ if (overlay) {
30
+ overlay.style.display = 'none';
31
+ }
32
+ }, 300);
33
+ }
34
+ }, 1000);
35
+
36
+ return () => clearTimeout(timeout);
37
+ }
38
+ }, [isTransitioning, targetTheme]);
39
+
40
+ if (!targetTheme) return null;
41
+
42
+ const Icon = targetTheme === 'light' ? IoSunnyOutline : IoMoonOutline;
43
+ // Sun: golden yellow, Moon: soft blue/white
44
+ const iconColor = targetTheme === 'light' ? '#fbbf24' : '#c7d2fe';
45
+
46
+ return (
47
+ <div
48
+ ref={overlayRef}
49
+ className="pc-theme-transition-overlay"
50
+ style={{
51
+ display: 'none',
52
+ opacity: 0,
53
+ position: 'fixed',
54
+ top: 0,
55
+ left: 0,
56
+ right: 0,
57
+ bottom: 0,
58
+ pointerEvents: 'none',
59
+ zIndex: 9999,
60
+ alignItems: 'center',
61
+ justifyContent: 'center',
62
+ transition: 'opacity 0.3s ease',
63
+ }}
64
+ aria-hidden="true"
65
+ >
66
+ <div
67
+ ref={iconRef}
68
+ className={`pc-theme-transition-icon pc-theme-transition-${targetTheme}`}
69
+ style={{
70
+ position: 'absolute',
71
+ bottom: '-150px',
72
+ left: '50%',
73
+ transform: 'translateX(-50%)',
74
+ fontSize: '120px',
75
+ color: iconColor,
76
+ display: 'flex',
77
+ alignItems: 'center',
78
+ justifyContent: 'center',
79
+ }}
80
+ >
81
+ <Icon size={120} />
82
+ </div>
83
+ </div>
84
+ );
85
+ }