@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.
Files changed (161) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/LICENSE +176 -0
  3. package/README.md +98 -1
  4. package/dist/index.js +231 -96
  5. package/dist/index.mjs +231 -96
  6. package/dist/privacy-center.d.mts +649 -0
  7. package/dist/privacy-center.d.ts +649 -0
  8. package/dist/privacy-center.js +20824 -0
  9. package/dist/privacy-center.mjs +20789 -0
  10. package/package.json +47 -3
  11. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
  12. package/src/RedactoNoticeConsent/api/index.ts +2 -0
  13. package/src/RedactoNoticeConsent/api/types.ts +2 -0
  14. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
  15. package/src/RedactoNoticeConsent/language-codes.ts +97 -0
  16. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
  17. package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
  18. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  19. package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
  20. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  21. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  22. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
  23. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
  24. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
  25. package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
  26. package/src/RedactoPrivacyCenter/api/client.ts +524 -0
  27. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  28. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  29. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  30. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  31. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  32. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  33. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  34. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  35. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  36. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  37. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  38. package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +38 -0
  39. package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
  40. package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
  41. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
  42. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
  44. package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
  45. package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
  46. package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
  47. package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
  48. package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
  49. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
  50. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
  51. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
  52. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -0
  53. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  54. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  55. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  56. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  57. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  58. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +304 -0
  59. package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
  60. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
  61. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
  62. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
  63. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  64. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
  65. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
  66. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  67. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -0
  68. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  69. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  70. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  71. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  72. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  73. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  74. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +159 -0
  75. package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
  76. package/src/RedactoPrivacyCenter/components/types.ts +76 -0
  77. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  78. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  79. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  80. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +137 -0
  81. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
  82. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  83. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -0
  84. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  85. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  86. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  87. package/src/RedactoPrivacyCenter/context/types.ts +14 -0
  88. package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
  89. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  90. package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
  91. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  92. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  93. package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
  94. package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
  95. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  96. package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
  97. package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
  98. package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
  99. package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
  100. package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
  101. package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
  102. package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
  103. package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
  104. package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
  105. package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
  106. package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
  107. package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
  108. package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
  109. package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
  110. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
  111. package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
  112. package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
  113. package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
  114. package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
  115. package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
  116. package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
  117. package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
  118. package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
  119. package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
  120. package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
  121. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  122. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
  123. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
  124. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  125. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  126. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
  127. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
  128. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  129. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  130. package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
  131. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  132. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -0
  133. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  134. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  135. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  136. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  137. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  138. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +107 -0
  139. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  140. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
  141. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
  142. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
  143. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
  144. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
  145. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
  146. package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -0
  147. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  148. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  149. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  150. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  151. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +53 -0
  152. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -0
  153. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  154. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  155. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  156. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  157. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  158. package/src/privacy-center.ts +108 -0
  159. package/tsconfig.json +2 -0
  160. package/tsup.config.ts +16 -1
  161. package/.turbo/turbo-build.log +0 -21
@@ -0,0 +1,408 @@
1
+ import { PCTitle, PCCollapse, PCTooltip } from '../../ui';
2
+ import { pcStyles } from '../../styles/pcStyles';
3
+ import { PiCaretDownBold } from 'react-icons/pi';
4
+ import { MdLineWeight } from 'react-icons/md';
5
+
6
+ const getVar = (varName: string): string => `var(${varName})`;
7
+ import StatusBadge from '../../ui/StatusBadge';
8
+ import { useInternalNavigation, type InternalPage } from '../../context/NavigationContext';
9
+ import { useTranslation } from 'react-i18next';
10
+ import type { MenuBadgeProps, SideMenuPCProps } from './types';
11
+ import { badges } from './constants';
12
+
13
+ const MenuBadge = ({ type, isDisabled }: MenuBadgeProps) => {
14
+ const { t } = useTranslation();
15
+ const badge = badges[type];
16
+ if (!badge || isDisabled) return null;
17
+
18
+ const isNewBadge = type === 'new';
19
+ const displayText = isNewBadge ? t('badgeNew') : badge.text;
20
+
21
+ return (
22
+ <span
23
+ style={{
24
+ marginLeft: getVar('--pc-spacing-sm'),
25
+ borderRadius: '9999px',
26
+ paddingLeft: '0.5rem',
27
+ paddingRight: '0.5rem',
28
+ paddingTop: '0.125rem',
29
+ paddingBottom: '0.125rem',
30
+ fontSize: pcStyles.textXs.fontSize,
31
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
32
+ ...(isNewBadge ? {
33
+ backgroundColor: getVar('--pc-bg-hover'),
34
+ color: getVar('--pc-primary'),
35
+ } : {
36
+ backgroundColor: getVar('--pc-bg-tertiary'),
37
+ color: getVar('--pc-text-secondary'),
38
+ }),
39
+ }}
40
+ >
41
+ {displayText}
42
+ </span>
43
+ );
44
+ };
45
+
46
+ export function SideMenuPC({ menuItems, isCollapsed, onItemSelect }: SideMenuPCProps) {
47
+ const { currentPage, navigateTo } = useInternalNavigation();
48
+
49
+ return (
50
+ <div
51
+ className="pc-sidebar-menu-container"
52
+ style={{
53
+ display: 'flex',
54
+ flexDirection: 'column',
55
+ paddingTop: '0.625rem',
56
+ paddingBottom: '0.625rem',
57
+ paddingLeft: '0.5rem',
58
+ paddingRight: '0.5rem',
59
+ width: '100%',
60
+ }}
61
+ >
62
+ {menuItems.map((item, index) => {
63
+ // Check if current page matches this item's href
64
+ const isActive = item?.href ? currentPage === (item.href as InternalPage) : false;
65
+ const pathnameExistInDropdowns = item?.dropdownItems?.filter(
66
+ (dropdownItem) => {
67
+ return currentPage === (dropdownItem.href as InternalPage);
68
+ }
69
+ );
70
+ const isDropdownOpen = Boolean(pathnameExistInDropdowns?.length);
71
+
72
+ // Handler for navigation
73
+ const handleNavigate = (href: string) => {
74
+ if (!item.isComingSoon && href) {
75
+ navigateTo(href as InternalPage);
76
+ onItemSelect?.();
77
+ }
78
+ };
79
+
80
+ const renderItemWithTooltip = (content: React.ReactNode) => {
81
+ if (isCollapsed) {
82
+ return (
83
+ <PCTooltip content={item.name} placement="right" color="invert">
84
+ <div style={{ width: 'fit-content', margin: '0 auto' }}>{content}</div>
85
+ </PCTooltip>
86
+ );
87
+ }
88
+ return content;
89
+ };
90
+
91
+ return (
92
+ <div
93
+ key={item.name + '-' + index}
94
+ style={{
95
+ width: '100%',
96
+ display: 'block',
97
+ marginBottom: isCollapsed ? '0.875rem' : '0.125rem',
98
+ }}
99
+ >
100
+ {item?.href ? (
101
+ <>
102
+ {item?.dropdownItems ? (
103
+ <PCCollapse
104
+ defaultOpen={isDropdownOpen}
105
+ >
106
+ {({ open, toggle }) => (
107
+ <>
108
+ <PCCollapse.Header
109
+ open={open}
110
+ toggle={toggle}
111
+ >
112
+ {renderItemWithTooltip(
113
+ <div
114
+ onClick={!item.isComingSoon ? toggle : undefined}
115
+ style={{
116
+ position: 'relative',
117
+ marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
118
+ marginBottom: isCollapsed ? 0 : '0.1875rem',
119
+ ...pcStyles.flex,
120
+ ...pcStyles.itemsCenter,
121
+ ...pcStyles.justifyCenter,
122
+ ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
123
+ borderRadius: pcStyles.borderRadiusMd,
124
+ padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
125
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
126
+ cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
127
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
128
+ backgroundColor: isDropdownOpen && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
129
+ opacity: item.isComingSoon ? 0.5 : 1,
130
+ transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
131
+ minHeight: isCollapsed ? '2rem' : '2.25rem',
132
+ width: isCollapsed ? 'fit-content' : '100%',
133
+ willChange: 'padding, justify-content',
134
+ }}
135
+ >
136
+ <div
137
+ style={{
138
+ ...pcStyles.flex,
139
+ ...pcStyles.itemsCenter,
140
+ ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
141
+ pointerEvents: item.isComingSoon ? 'none' : 'auto',
142
+ transition: 'justify-content 0.3s ease, width 0.3s ease',
143
+ willChange: 'justify-content, width',
144
+ }}
145
+ >
146
+ {item?.icon && (
147
+ <span
148
+ style={{
149
+ marginRight: isCollapsed ? 0 : getVar('--pc-spacing-sm'),
150
+ display: 'inline-flex',
151
+ height: '1.25rem',
152
+ width: '1.25rem',
153
+ ...pcStyles.itemsCenter,
154
+ ...pcStyles.justifyCenter,
155
+ borderRadius: pcStyles.borderRadiusMd,
156
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
157
+ transition: 'margin-right 0.3s ease',
158
+ willChange: 'margin-right',
159
+ }}
160
+ >
161
+ {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
162
+ </span>
163
+ )}
164
+ <span style={{
165
+ overflow: 'hidden',
166
+ textOverflow: 'ellipsis',
167
+ whiteSpace: 'nowrap',
168
+ marginLeft: isCollapsed ? 0 : '0.5rem',
169
+ opacity: isCollapsed ? 0 : 1,
170
+ width: isCollapsed ? 0 : 'auto',
171
+ maxWidth: isCollapsed ? 0 : '100%',
172
+ transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
173
+ willChange: 'opacity, width, max-width, margin-left',
174
+ }}>
175
+ {item.name}
176
+ {item.isNew && (
177
+ <MenuBadge
178
+ type="new"
179
+ isDisabled={item.isComingSoon}
180
+ />
181
+ )}
182
+ </span>
183
+ </div>
184
+
185
+ <div style={{
186
+ opacity: (!item.isComingSoon && !isCollapsed) ? 1 : 0,
187
+ width: (!item.isComingSoon && !isCollapsed) ? 'auto' : 0,
188
+ overflow: 'hidden',
189
+ transition: 'opacity 0.3s ease, width 0.3s ease',
190
+ willChange: 'opacity, width',
191
+ flexShrink: 0,
192
+ }}>
193
+ {!item.isComingSoon && (
194
+ <PiCaretDownBold
195
+ strokeWidth={3}
196
+ size={14}
197
+ style={{
198
+ transform: open ? 'rotate(0deg)' : 'rotate(-90deg)',
199
+ transition: 'transform 0.2s ease',
200
+ color: getVar('--pc-text-secondary'),
201
+ }}
202
+ />
203
+ )}
204
+ </div>
205
+ </div>
206
+ )}
207
+ </PCCollapse.Header>
208
+ <PCCollapse.Body open={open}>
209
+ {!isCollapsed &&
210
+ item?.dropdownItems?.map((dropdownItem, index) => {
211
+ const isChildActive = currentPage === (dropdownItem.href as InternalPage);
212
+
213
+ return renderItemWithTooltip(
214
+ <div
215
+ key={dropdownItem?.name + index}
216
+ onClick={() => handleNavigate(dropdownItem.href)}
217
+ style={{
218
+ marginLeft: '1.25rem',
219
+ marginRight: '0.625rem',
220
+ marginTop: index === 0 ? '0.1875rem' : '0.125rem',
221
+ marginBottom: '0.125rem',
222
+ ...pcStyles.flex,
223
+ ...pcStyles.itemsCenter,
224
+ borderRadius: pcStyles.borderRadiusMd,
225
+ padding: '0.4375rem 0.75rem',
226
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
227
+ textTransform: 'capitalize',
228
+ cursor: dropdownItem.isComingSoon ? 'not-allowed' : 'pointer',
229
+ color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-secondary'),
230
+ backgroundColor: isChildActive && !dropdownItem.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
231
+ opacity: dropdownItem.isComingSoon ? 0.5 : 1,
232
+ transition: 'color 0.2s, background-color 0.2s',
233
+ minHeight: '2rem',
234
+ }}
235
+ >
236
+ <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter }}>
237
+ <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
238
+ {dropdownItem?.icon && (
239
+ <span
240
+ style={{
241
+ marginRight: getVar('--pc-spacing-sm'),
242
+ display: 'inline-flex',
243
+ height: '1.125rem',
244
+ width: '1.125rem',
245
+ ...pcStyles.itemsCenter,
246
+ ...pcStyles.justifyCenter,
247
+ borderRadius: pcStyles.borderRadiusMd,
248
+ color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
249
+ }}
250
+ >
251
+ {dropdownItem.icon}
252
+ </span>
253
+ )}
254
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
255
+ {dropdownItem?.name}
256
+ </span>
257
+ {dropdownItem.isNew && (
258
+ <MenuBadge
259
+ type="new"
260
+ isDisabled={dropdownItem.isComingSoon}
261
+ />
262
+ )}
263
+ </div>
264
+ <div style={{ marginLeft: getVar('--pc-spacing-md'), ...pcStyles.flex, ...pcStyles.itemsCenter }}>
265
+ {dropdownItem?.badge?.length ? (
266
+ <StatusBadge status={dropdownItem?.badge} className="pc-status-badge" />
267
+ ) : (
268
+ <div style={{ width: '1rem' }} />
269
+ )}
270
+ </div>
271
+ </div>
272
+ </div>
273
+ );
274
+ })}
275
+ </PCCollapse.Body>
276
+ </>
277
+ )}
278
+ </PCCollapse>
279
+ ) : (
280
+ renderItemWithTooltip(
281
+ <div
282
+ key={item.name + '-' + index}
283
+ onClick={() => item.href && handleNavigate(item.href)}
284
+ style={{
285
+ position: 'relative',
286
+ marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
287
+ marginBottom: isCollapsed ? 0 : '0.1875rem',
288
+ ...pcStyles.flex,
289
+ ...pcStyles.itemsCenter,
290
+ ...pcStyles.justifyCenter,
291
+ ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
292
+ borderRadius: pcStyles.borderRadiusMd,
293
+ padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
294
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
295
+ textTransform: 'capitalize',
296
+ cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
297
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
298
+ backgroundColor: isActive && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
299
+ opacity: item.isComingSoon ? 0.5 : 1,
300
+ transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
301
+ minHeight: isCollapsed ? '2rem' : '2.25rem',
302
+ width: isCollapsed ? 'fit-content' : '100%',
303
+ willChange: 'padding, justify-content',
304
+ }}
305
+ >
306
+ <div style={{
307
+ ...pcStyles.flex,
308
+ ...pcStyles.itemsCenter,
309
+ ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
310
+ transition: 'justify-content 0.3s ease, width 0.3s ease',
311
+ willChange: 'justify-content, width',
312
+ }}>
313
+ {item?.icon && (
314
+ <span
315
+ style={{
316
+ marginRight: isCollapsed ? 0 : getVar('--pc-spacing-sm'),
317
+ display: 'inline-flex',
318
+ height: '1.25rem',
319
+ width: '1.25rem',
320
+ ...pcStyles.itemsCenter,
321
+ ...pcStyles.justifyCenter,
322
+ borderRadius: pcStyles.borderRadiusMd,
323
+ color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
324
+ transition: 'margin-right 0.3s ease',
325
+ willChange: 'margin-right',
326
+ }}
327
+ >
328
+ {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
329
+ </span>
330
+ )}
331
+ <span style={{
332
+ overflow: 'hidden',
333
+ textOverflow: 'ellipsis',
334
+ whiteSpace: 'nowrap',
335
+ marginLeft: isCollapsed ? 0 : '0.5rem',
336
+ opacity: isCollapsed ? 0 : 1,
337
+ width: isCollapsed ? 0 : 'auto',
338
+ maxWidth: isCollapsed ? 0 : '100%',
339
+ transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
340
+ willChange: 'opacity, width, max-width, margin-left',
341
+ }}>
342
+ {item.name}
343
+ {item.isNew && <MenuBadge type="new" />}
344
+ </span>
345
+ </div>
346
+ <div style={{
347
+ opacity: (!isCollapsed && item?.badge?.length) ? 1 : 0,
348
+ width: (!isCollapsed && item?.badge?.length) ? 'auto' : 0,
349
+ overflow: 'hidden',
350
+ transition: 'opacity 0.3s ease, width 0.3s ease',
351
+ willChange: 'opacity, width',
352
+ flexShrink: 0,
353
+ }}>
354
+ {item?.badge?.length ? <StatusBadge status={item?.badge} className="pc-status-badge" /> : null}
355
+ </div>
356
+ </div>
357
+ )
358
+ )}
359
+ </>
360
+ ) : (
361
+ <>
362
+ {isCollapsed ? (
363
+ <div
364
+ style={{
365
+ marginLeft: '0.75rem',
366
+ marginRight: '0.75rem',
367
+ marginTop: index !== 0 ? '1rem' : '0.5rem',
368
+ marginBottom: '0.5rem',
369
+ height: '1px',
370
+ }}
371
+ >
372
+ <hr style={{
373
+ width: '100%',
374
+ backgroundColor: getVar('--pc-border-color'),
375
+ border: 'none',
376
+ height: '1px',
377
+ margin: 0,
378
+ }} />
379
+ </div>
380
+ ) : (
381
+ <PCTitle
382
+ as="h6"
383
+ style={{
384
+ marginTop: index !== 0 ? '1.25rem' : '0.5rem',
385
+ marginBottom: '0.375rem',
386
+ overflow: 'hidden',
387
+ textOverflow: 'ellipsis',
388
+ whiteSpace: 'nowrap',
389
+ paddingLeft: '1rem',
390
+ paddingRight: '1rem',
391
+ fontSize: pcStyles.textXs.fontSize,
392
+ fontWeight: pcStyles.fontWeightMedium.fontWeight,
393
+ textTransform: 'uppercase',
394
+ letterSpacing: '0.1em',
395
+ color: getVar('--pc-text-secondary'),
396
+ }}
397
+ >
398
+ {item.name}
399
+ </PCTitle>
400
+ )}
401
+ </>
402
+ )}
403
+ </div>
404
+ );
405
+ })}
406
+ </div>
407
+ );
408
+ }
@@ -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 type { 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
+ }