@redacto.io/consent-sdk-react 4.0.0 → 5.0.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 (160) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/LICENSE +176 -0
  3. package/dist/index.js +231 -96
  4. package/dist/index.mjs +231 -96
  5. package/dist/privacy-center.d.mts +649 -0
  6. package/dist/privacy-center.d.ts +649 -0
  7. package/dist/privacy-center.js +20824 -0
  8. package/dist/privacy-center.mjs +20789 -0
  9. package/package.json +47 -3
  10. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +6 -4
  11. package/src/RedactoNoticeConsent/api/index.ts +2 -0
  12. package/src/RedactoNoticeConsent/api/types.ts +2 -0
  13. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +128 -62
  14. package/src/RedactoNoticeConsent/language-codes.ts +97 -0
  15. package/src/RedactoNoticeConsentInline/RedactoNoticeConsentInline.tsx +0 -3
  16. package/src/RedactoNoticeConsentInline/api/index.ts +0 -1
  17. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  18. package/src/RedactoPrivacyCenter/PrivacyCenterCaseDetailsPage.tsx +154 -0
  19. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  20. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  21. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +30 -0
  22. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +173 -0
  23. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +55 -0
  24. package/src/RedactoPrivacyCenter/api/actions.ts +476 -0
  25. package/src/RedactoPrivacyCenter/api/client.ts +524 -0
  26. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  27. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  28. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  29. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  30. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  31. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  32. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  33. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  34. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  35. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  36. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  37. package/src/RedactoPrivacyCenter/components/CaseDetails/AttachmentCard.tsx +38 -0
  38. package/src/RedactoPrivacyCenter/components/CaseDetails/ChatBubble.tsx +31 -0
  39. package/src/RedactoPrivacyCenter/components/CaseDetails/DocumentRequestCard.tsx +73 -0
  40. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageComposer.tsx +87 -0
  41. package/src/RedactoPrivacyCenter/components/CaseDetails/MessageItem.tsx +95 -0
  42. package/src/RedactoPrivacyCenter/components/CaseDetails/MessagesTab.tsx +147 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetails/RequestDetailsTab.tsx +171 -0
  44. package/src/RedactoPrivacyCenter/components/CaseDetails/StatusEvent.tsx +23 -0
  45. package/src/RedactoPrivacyCenter/components/CaseDetails/useFileUpload.ts +116 -0
  46. package/src/RedactoPrivacyCenter/components/CaseDetails/useMessagePolling.ts +36 -0
  47. package/src/RedactoPrivacyCenter/components/CaseDetails/utils.ts +56 -0
  48. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +469 -0
  49. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +426 -0
  50. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +732 -0
  51. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +238 -0
  52. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  53. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  54. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  55. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  56. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  57. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +304 -0
  58. package/src/RedactoPrivacyCenter/components/Form.tsx +1596 -0
  59. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +174 -0
  60. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +337 -0
  61. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +330 -0
  62. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  63. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +304 -0
  64. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +232 -0
  65. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  66. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +408 -0
  67. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  68. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  69. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  70. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  71. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  72. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  73. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +159 -0
  74. package/src/RedactoPrivacyCenter/components/constant.ts +16 -0
  75. package/src/RedactoPrivacyCenter/components/types.ts +76 -0
  76. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  77. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  78. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  79. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +137 -0
  80. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +41 -0
  81. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  82. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +96 -0
  83. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  84. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  85. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  86. package/src/RedactoPrivacyCenter/context/types.ts +14 -0
  87. package/src/RedactoPrivacyCenter/lib/api.ts +566 -0
  88. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  89. package/src/RedactoPrivacyCenter/lib/constants.ts +161 -0
  90. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  91. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  92. package/src/RedactoPrivacyCenter/lib/queries.ts +134 -0
  93. package/src/RedactoPrivacyCenter/lib/types.ts +531 -0
  94. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  95. package/src/RedactoPrivacyCenter/lib/utils.ts +626 -0
  96. package/src/RedactoPrivacyCenter/locales/as/translation.json +280 -0
  97. package/src/RedactoPrivacyCenter/locales/bho/translation.json +280 -0
  98. package/src/RedactoPrivacyCenter/locales/bn/translation.json +280 -0
  99. package/src/RedactoPrivacyCenter/locales/brx/translation.json +280 -0
  100. package/src/RedactoPrivacyCenter/locales/doi/translation.json +280 -0
  101. package/src/RedactoPrivacyCenter/locales/en/translation.json +255 -0
  102. package/src/RedactoPrivacyCenter/locales/gom/translation.json +280 -0
  103. package/src/RedactoPrivacyCenter/locales/gu/translation.json +280 -0
  104. package/src/RedactoPrivacyCenter/locales/hi/translation.json +280 -0
  105. package/src/RedactoPrivacyCenter/locales/kn/translation.json +280 -0
  106. package/src/RedactoPrivacyCenter/locales/ks/translation.json +280 -0
  107. package/src/RedactoPrivacyCenter/locales/mai/translation.json +280 -0
  108. package/src/RedactoPrivacyCenter/locales/ml/translation.json +280 -0
  109. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +280 -0
  110. package/src/RedactoPrivacyCenter/locales/mr/translation.json +280 -0
  111. package/src/RedactoPrivacyCenter/locales/ne/translation.json +280 -0
  112. package/src/RedactoPrivacyCenter/locales/or/translation.json +280 -0
  113. package/src/RedactoPrivacyCenter/locales/pa/translation.json +280 -0
  114. package/src/RedactoPrivacyCenter/locales/sa/translation.json +280 -0
  115. package/src/RedactoPrivacyCenter/locales/sat/translation.json +280 -0
  116. package/src/RedactoPrivacyCenter/locales/sd/translation.json +280 -0
  117. package/src/RedactoPrivacyCenter/locales/ta/translation.json +280 -0
  118. package/src/RedactoPrivacyCenter/locales/te/translation.json +280 -0
  119. package/src/RedactoPrivacyCenter/locales/ur/translation.json +280 -0
  120. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  121. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1725 -0
  122. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +813 -0
  123. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  124. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  125. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +367 -0
  126. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +476 -0
  127. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  128. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  129. package/src/RedactoPrivacyCenter/ui/Footer.tsx +75 -0
  130. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  131. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +93 -0
  132. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  133. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  134. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  135. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  136. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  137. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +107 -0
  138. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  139. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +132 -0
  140. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +31 -0
  141. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +106 -0
  142. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +173 -0
  143. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +387 -0
  144. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +149 -0
  145. package/src/RedactoPrivacyCenter/ui/PCText.tsx +41 -0
  146. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  147. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  148. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  149. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  150. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +53 -0
  151. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +72 -0
  152. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  153. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  154. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  155. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  156. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  157. package/src/privacy-center.ts +108 -0
  158. package/tsconfig.json +2 -0
  159. package/tsup.config.ts +16 -1
  160. package/.turbo/turbo-build.log +0 -21
@@ -0,0 +1,426 @@
1
+ import { useState, useMemo, useCallback, useEffect } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ PCTitle,
5
+ PCText,
6
+ PCButton,
7
+ PCInput,
8
+ PCLoader,
9
+ } from '../ui';
10
+ import { pcStyles } from '../styles/pcStyles';
11
+ import { FiSearch, FiFilter } from 'react-icons/fi';
12
+ import { IoAdd } from 'react-icons/io5';
13
+ import type { CaseRequest } from '../lib/types';
14
+ import { RequestTypeEnum } from '../lib/types';
15
+ import { useCaseHistory } from '../lib/queries';
16
+ import { useOrganization } from '../context/OrganizationContext';
17
+ import { useAuth } from '../context/AuthContext';
18
+ import { useRequestTypeLabels, useGrievanceTabs } from '../lib/constants';
19
+ import type { CaseHistoryProps } from './types';
20
+ import { GRIEVANCE_TAB_IDS } from './constant';
21
+ import { useInternalNavigation } from '../context/NavigationContext';
22
+ import { getCoreRowModel, useReactTable } from '@tanstack/react-table';
23
+ import { useCaseHistoryColumns } from './caseHistoryColumns';
24
+ import { PCPopover, PCCheckbox } from '../ui';
25
+ import GrievanceTable from './GrievanceTable';
26
+ import GrievanceTablePagination from './GrievanceTablePagination';
27
+
28
+ export default function CaseHistory({ onCreateRequest }: CaseHistoryProps) {
29
+ const { t } = useTranslation();
30
+ const { navigateTo, setSelectedCase } = useInternalNavigation();
31
+ const grievanceTabs = useGrievanceTabs();
32
+ const requestTypeLabels = useRequestTypeLabels();
33
+ const [activeTab, setActiveTab] = useState<string>(GRIEVANCE_TAB_IDS.ALL);
34
+ const [searchQuery, setSearchQuery] = useState('');
35
+ const [selectedRequestTypes, setSelectedRequestTypes] = useState<string[]>(
36
+ Object.values(RequestTypeEnum)
37
+ );
38
+ const [pageIndex, setPageIndex] = useState(0);
39
+ const [pageSize, setPageSize] = useState(10);
40
+ const { organization } = useOrganization();
41
+ const { accessToken, refreshToken, onError, updateTokens } = useAuth();
42
+ const organizationId = organization?.uuid;
43
+ const workspaceId = organization?.workspaces[0]?.uuid;
44
+ const contact =
45
+ typeof window !== 'undefined' ? sessionStorage.getItem('email') || '' : '';
46
+
47
+ // Create auth context object for API calls - memoized to prevent infinite loops
48
+ const authContext = useMemo(() => ({
49
+ accessToken,
50
+ refreshToken,
51
+ onError,
52
+ updateTokens,
53
+ }), [accessToken, refreshToken, onError, updateTokens]);
54
+
55
+ const {
56
+ data: cases,
57
+ isLoading,
58
+ error,
59
+ isError,
60
+ } = useCaseHistory(organizationId, workspaceId, contact, authContext);
61
+
62
+ const filteredCases = useMemo(() => {
63
+ return (cases || []).filter((item: CaseRequest) => {
64
+ const statusLower = item.status.toLowerCase();
65
+
66
+ // Filter by tab (status filter)
67
+ if (activeTab !== GRIEVANCE_TAB_IDS.ALL) {
68
+ if (
69
+ activeTab === GRIEVANCE_TAB_IDS.PROCESSING &&
70
+ !['processing', 'in_progress', 'initiated', 'assigned'].includes(
71
+ statusLower
72
+ )
73
+ ) {
74
+ return false;
75
+ }
76
+ if (activeTab === GRIEVANCE_TAB_IDS.COMPLETED && statusLower !== 'completed') {
77
+ return false;
78
+ }
79
+ if (
80
+ activeTab === GRIEVANCE_TAB_IDS.REJECTED &&
81
+ !['rejected', 'declined'].includes(statusLower)
82
+ ) {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ // Filter by request type
88
+ // Only apply filter if some types are selected (not all)
89
+ const allTypes = Object.values(RequestTypeEnum);
90
+ const hasAllTypesSelected = selectedRequestTypes.length === allTypes.length &&
91
+ allTypes.every(type => selectedRequestTypes.includes(type));
92
+
93
+ if (!hasAllTypesSelected && selectedRequestTypes.length > 0) {
94
+ const itemRequestType = item.request_type?.toUpperCase();
95
+ if (!itemRequestType || !selectedRequestTypes.some(rt => rt.toUpperCase() === itemRequestType)) {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ // Filter by search
101
+ if (searchQuery) {
102
+ const query = searchQuery.toLowerCase();
103
+ const matchesId = item.case_id?.toLowerCase().includes(query);
104
+ const matchesDesc =
105
+ item.description?.toLowerCase().includes(query) ||
106
+ item.request_details?.purposes?.[0]?.purpose?.description
107
+ ?.toLowerCase()
108
+ .includes(query);
109
+
110
+ if (!matchesId && !matchesDesc) return false;
111
+ }
112
+ return true;
113
+ });
114
+ }, [cases, activeTab, selectedRequestTypes, searchQuery]);
115
+
116
+ const handleViewDetails = useCallback((caseRequest: CaseRequest) => {
117
+ setSelectedCase(caseRequest);
118
+ navigateTo('case-details');
119
+ }, [navigateTo, setSelectedCase]);
120
+
121
+ const columns = useCaseHistoryColumns();
122
+
123
+ // Reset to first page when filters change (but not when pagination changes)
124
+ useEffect(() => {
125
+ setPageIndex(0);
126
+ }, [activeTab, searchQuery, selectedRequestTypes]);
127
+
128
+ const table = useReactTable<CaseRequest>({
129
+ data: filteredCases,
130
+ columns: columns,
131
+ getCoreRowModel: getCoreRowModel(),
132
+ manualPagination: false, // Client-side pagination - we'll handle it manually
133
+ getRowId: (row) => row.uuid || row.case_id,
134
+ state: {
135
+ pagination: {
136
+ pageIndex: pageIndex,
137
+ pageSize: pageSize,
138
+ },
139
+ },
140
+ onPaginationChange: (updater) => {
141
+ const currentState = { pageIndex, pageSize };
142
+ const next =
143
+ typeof updater === 'function'
144
+ ? updater(currentState)
145
+ : updater;
146
+ const nextPageIndex = (next as any).pageIndex;
147
+ const nextPageSize = (next as any).pageSize;
148
+
149
+ // Update state directly
150
+ if (typeof nextPageIndex === 'number') {
151
+ setPageIndex(nextPageIndex);
152
+ }
153
+ if (typeof nextPageSize === 'number') {
154
+ setPageSize(nextPageSize);
155
+ // Reset to first page when page size changes
156
+ setPageIndex(0);
157
+ }
158
+ },
159
+ meta: {
160
+ onRowClick: handleViewDetails,
161
+ },
162
+ });
163
+
164
+ if (isLoading) {
165
+ return (
166
+ <div style={{
167
+ ...pcStyles.flex,
168
+ height: '24rem',
169
+ width: '100%',
170
+ ...pcStyles.itemsCenter,
171
+ ...pcStyles.justifyCenter,
172
+ }}>
173
+ <PCLoader variant="spinner" size="lg" />
174
+ </div>
175
+ );
176
+ }
177
+
178
+ if (isError) {
179
+ return (
180
+ <div style={{
181
+ ...pcStyles.flex,
182
+ ...pcStyles.flexCol,
183
+ height: '24rem',
184
+ width: '100%',
185
+ ...pcStyles.itemsCenter,
186
+ ...pcStyles.justifyCenter,
187
+ }}>
188
+ <PCText style={{ marginBottom: pcStyles.mbMd.marginBottom, color: '#dc2626' }}>
189
+ {t('errorLoadingCaseHistory')}
190
+ </PCText>
191
+ <PCText size="sm" color="secondary">
192
+ {error?.message || t('unknownError')}
193
+ </PCText>
194
+ </div>
195
+ );
196
+ }
197
+
198
+ if (!contact) {
199
+ return (
200
+ <div style={{
201
+ ...pcStyles.flex,
202
+ ...pcStyles.flexCol,
203
+ height: '24rem',
204
+ width: '100%',
205
+ ...pcStyles.itemsCenter,
206
+ ...pcStyles.justifyCenter,
207
+ }}>
208
+ <PCText style={{ marginBottom: pcStyles.mbMd.marginBottom, color: '#6b7280' }}>
209
+ {t('pleaseSignInToViewGrievance')}
210
+ </PCText>
211
+ <PCText size="sm" color="secondary">
212
+ {t('emailNotFoundInSession')}
213
+ </PCText>
214
+ </div>
215
+ );
216
+ }
217
+
218
+ const getVar = (varName: string): string => `var(${varName})`;
219
+
220
+ return (
221
+ <div style={{ width: '100%' }}>
222
+ {/* Top Actions */}
223
+ <div style={{
224
+ display: 'flex',
225
+ justifyContent: 'flex-end',
226
+ alignItems: 'center',
227
+ paddingTop: '0.5rem',
228
+ marginBottom: getVar('--pc-spacing-md'),
229
+ width: '100%',
230
+ }}>
231
+ <div style={{
232
+ display: 'flex',
233
+ width: '100%',
234
+ justifyContent: 'flex-end',
235
+ }} className="pc-export-button-responsive">
236
+ <PCButton
237
+ size="sm"
238
+ onClick={onCreateRequest}
239
+ style={{
240
+ whiteSpace: 'nowrap',
241
+ width: '100%',
242
+ ...pcStyles.gapSm,
243
+ }}
244
+ >
245
+ <IoAdd size={15} /> <span>{t('newRequest')}</span>
246
+ </PCButton>
247
+ </div>
248
+ </div>
249
+
250
+ {/* Filters Section: Search on left, Tabs and Filter on rightmost - Responsive */}
251
+ <div style={{
252
+ display: 'flex',
253
+ flexDirection: 'column',
254
+ gap: getVar('--pc-spacing-md'),
255
+ marginBottom: getVar('--pc-spacing-lg'),
256
+ width: '100%',
257
+ }} className="pc-filters-responsive">
258
+ {/* Search Input - Full width on mobile, constrained on desktop */}
259
+ <div style={{
260
+ width: '100%',
261
+ }} className="pc-search-responsive">
262
+ <PCInput
263
+ type="search"
264
+ clearable
265
+ clearAriaLabel={t('clearInput')}
266
+ placeholder={t('searchCasesByID')}
267
+ value={searchQuery}
268
+ onClear={() => setSearchQuery('')}
269
+ prefix={<FiSearch size={20} strokeWidth={1.2} />}
270
+ onChange={(e) => {
271
+ setSearchQuery(e.target.value);
272
+ }}
273
+ style={{
274
+ width: '100%',
275
+ }}
276
+ />
277
+ </div>
278
+
279
+ {/* Tabs and Filter Dropdown - Rightmost, Stacked on mobile, horizontal on desktop */}
280
+ <div style={{
281
+ display: 'flex',
282
+ flexDirection: 'column',
283
+ gap: getVar('--pc-spacing-sm'),
284
+ }} className="pc-filters-row-responsive">
285
+ {/* Tabs */}
286
+ <div style={{
287
+ ...pcStyles.overflowXAuto,
288
+ flexShrink: 1,
289
+ minWidth: 0,
290
+ }}>
291
+ <div style={{
292
+ display: 'inline-flex',
293
+ minWidth: 'max-content',
294
+ ...pcStyles.itemsCenter,
295
+ gap: '0.25rem',
296
+ borderRadius: '0.5rem',
297
+ backgroundColor: getVar('--pc-bg-secondary'),
298
+ padding: '0.375rem',
299
+ border: `1px solid ${getVar('--pc-border-color')}`,
300
+ }}>
301
+ {grievanceTabs.map((tab) => (
302
+ <button
303
+ key={tab.id}
304
+ style={{
305
+ display: 'inline-flex',
306
+ ...pcStyles.itemsCenter,
307
+ gap: '0.5rem',
308
+ borderRadius: '0.375rem',
309
+ padding: '0.375rem 0.75rem',
310
+ fontSize: pcStyles.textXs.fontSize,
311
+ fontWeight: pcStyles.fontWeightSemibold.fontWeight,
312
+ transition: 'all 0.2s',
313
+ backgroundColor: activeTab === tab.id ? getVar('--pc-primary') : 'transparent',
314
+ color: activeTab === tab.id ? '#ffffff' : getVar('--pc-text-secondary'),
315
+ border: 'none',
316
+ cursor: 'pointer',
317
+ whiteSpace: 'nowrap',
318
+ }}
319
+ onClick={() => {
320
+ setActiveTab(tab.id);
321
+ }}
322
+ >
323
+ {tab.label}
324
+ </button>
325
+ ))}
326
+ </div>
327
+ </div>
328
+
329
+ {/* Filter Dropdown */}
330
+ <div style={{
331
+ flexShrink: 0,
332
+ display: 'flex',
333
+ }} className="pc-filter-button-wrapper">
334
+ <PCPopover
335
+ shadow="lg"
336
+ placement="bottom-end"
337
+ content={
338
+ <div style={{
339
+ minWidth: '160px',
340
+ padding: '0.75rem',
341
+ maxHeight: 'none',
342
+ overflow: 'visible'
343
+ }}>
344
+ <div style={{
345
+ ...pcStyles.flex,
346
+ ...pcStyles.flexCol,
347
+ gap: '0.5rem'
348
+ }}>
349
+ <PCTitle
350
+ as="h6"
351
+ style={{
352
+ marginBottom: '0.5rem',
353
+ paddingLeft: '0.125rem',
354
+ fontSize: pcStyles.textSm.fontSize,
355
+ fontWeight: pcStyles.fontWeightSemibold.fontWeight
356
+ }}
357
+ >
358
+ {t('filterByType')}
359
+ </PCTitle>
360
+ {Object.values(RequestTypeEnum).map((type) => (
361
+ <div
362
+ key={type}
363
+ style={{
364
+ ...pcStyles.flex,
365
+ ...pcStyles.itemsCenter,
366
+ padding: '0.375rem 0.25rem',
367
+ minHeight: '2rem'
368
+ }}
369
+ >
370
+ <PCCheckbox
371
+ label={requestTypeLabels[type]}
372
+ checked={selectedRequestTypes.includes(type)}
373
+ onChange={(e) => {
374
+ if (e.target.checked) {
375
+ setSelectedRequestTypes([
376
+ ...selectedRequestTypes,
377
+ type,
378
+ ]);
379
+ } else {
380
+ setSelectedRequestTypes(
381
+ selectedRequestTypes.filter((rt) => rt !== type)
382
+ );
383
+ }
384
+ }}
385
+ />
386
+ </div>
387
+ ))}
388
+ </div>
389
+ </div>
390
+ }
391
+ >
392
+ <PCButton
393
+ variant="outline"
394
+ style={{
395
+ width: 'auto',
396
+ justifyContent: 'space-between',
397
+ ...pcStyles.gapSm,
398
+ }}
399
+ >
400
+ <span>{t('filter')}</span>
401
+ <FiFilter size={16} />
402
+ </PCButton>
403
+ </PCPopover>
404
+ </div>
405
+ </div>
406
+ </div>
407
+ <GrievanceTable
408
+ key={`grievance-table-${activeTab}-${selectedRequestTypes.join(',')}-${searchQuery}`}
409
+ table={table}
410
+ isLoading={isLoading}
411
+ pageIndex={pageIndex}
412
+ pageSize={pageSize}
413
+ />
414
+
415
+ {/* Always show pagination, even during loading */}
416
+ <GrievanceTablePagination
417
+ table={table}
418
+ onPageIndexChange={setPageIndex}
419
+ onPageSizeChange={(newSize) => {
420
+ setPageSize(newSize);
421
+ setPageIndex(0);
422
+ }}
423
+ />
424
+ </div>
425
+ );
426
+ }