@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,174 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { flexRender } from '@tanstack/react-table';
4
+ import { PCTable, PCLoader, PCEmpty } from '../ui';
5
+ import { pcStyles } from '../styles/pcStyles';
6
+ import type { Table as ReactTableType } from '@tanstack/react-table';
7
+ import type { TableColumnMeta } from './types';
8
+
9
+ const getVar = (varName: string): string => `var(${varName})`;
10
+
11
+ export type GrievanceTableProps<T extends Record<string, any>> = {
12
+ table: ReactTableType<T>;
13
+ isLoading?: boolean;
14
+ className?: string;
15
+ pageIndex: number;
16
+ pageSize: number;
17
+ };
18
+
19
+ export default function GrievanceTable<TData extends Record<string, any>>({
20
+ table,
21
+ isLoading = false,
22
+ className,
23
+ pageIndex,
24
+ pageSize,
25
+ }: GrievanceTableProps<TData>) {
26
+ const { t } = useTranslation();
27
+
28
+ if (!table) return null;
29
+
30
+ // Get all rows and manually paginate
31
+ // Get rows directly from table - this will update when data changes
32
+ const rowModel = table.getRowModel();
33
+ const allRows = rowModel.rows;
34
+
35
+ // Manually paginate rows from the current row model
36
+ const paginatedRows = useMemo(() => {
37
+ const start = pageIndex * pageSize;
38
+ const end = start + pageSize;
39
+ return allRows.slice(start, end);
40
+ }, [allRows, pageIndex, pageSize]);
41
+
42
+ return (
43
+ <div
44
+ className={className}
45
+ style={{
46
+ ...pcStyles.wFull,
47
+ maxWidth: '100%',
48
+ ...pcStyles.overflowXAuto,
49
+ borderRadius: getVar('--pc-border-radius-md'),
50
+ border: `1px solid ${getVar('--pc-border-color')}`,
51
+ backgroundColor: getVar('--pc-bg-primary'),
52
+ transition: 'background-color 0.3s ease, border-color 0.3s ease',
53
+ }}
54
+ >
55
+ <PCTable
56
+ variant="modern"
57
+ style={{
58
+ minWidth: '100%',
59
+ backgroundColor: getVar('--pc-bg-primary'),
60
+ }}
61
+ >
62
+ <PCTable.Header>
63
+ {table.getHeaderGroups().map((headerGroup) => (
64
+ <PCTable.Row key={headerGroup.id}>
65
+ {headerGroup.headers.map((header) => (
66
+ <PCTable.Head
67
+ key={header.id}
68
+ style={{
69
+ padding: getVar('--pc-spacing-md'),
70
+ fontSize: pcStyles.textSm.fontSize,
71
+ fontWeight: pcStyles.fontWeightSemibold.fontWeight,
72
+ color: getVar('--pc-text-primary'),
73
+ backgroundColor: getVar('--pc-bg-secondary'),
74
+ borderBottom: `1px solid ${getVar('--pc-border-color')}`,
75
+ textAlign: (header.column.columnDef.meta as TableColumnMeta | undefined)?.align || 'left',
76
+ whiteSpace: 'nowrap',
77
+ }}
78
+ >
79
+ {header.isPlaceholder
80
+ ? null
81
+ : flexRender(
82
+ header.column.columnDef.header,
83
+ header.getContext()
84
+ )}
85
+ </PCTable.Head>
86
+ ))}
87
+ </PCTable.Row>
88
+ ))}
89
+ </PCTable.Header>
90
+
91
+ <PCTable.Body>
92
+ {isLoading ? (
93
+ <PCTable.Row>
94
+ <PCTable.Cell
95
+ colSpan={table.getAllColumns().length}
96
+ style={{
97
+ padding: getVar('--pc-spacing-2xl'),
98
+ textAlign: 'center',
99
+ }}
100
+ >
101
+ <PCLoader variant="spinner" size="lg" />
102
+ </PCTable.Cell>
103
+ </PCTable.Row>
104
+ ) : paginatedRows.length === 0 ? (
105
+ <PCTable.Row>
106
+ <PCTable.Cell
107
+ colSpan={table.getAllColumns().length}
108
+ style={{
109
+ padding: getVar('--pc-spacing-2xl'),
110
+ textAlign: 'center',
111
+ }}
112
+ >
113
+ <PCEmpty
114
+ icon={
115
+ <svg
116
+ viewBox="0 0 24 24"
117
+ fill="currentColor"
118
+ style={{
119
+ width: '64px',
120
+ height: '64px',
121
+ color: getVar('--pc-text-tertiary'),
122
+ transition: 'color 0.3s ease',
123
+ }}
124
+ >
125
+ <path d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h16a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1v-2zM3 16a1 1 0 011-1h16a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1v-2z" />
126
+ </svg>
127
+ }
128
+ title={t('noData')}
129
+ />
130
+ </PCTable.Cell>
131
+ </PCTable.Row>
132
+ ) : (
133
+ paginatedRows.map((row) => {
134
+ const meta = (table.options.meta as any) || {};
135
+ const onRowClick = meta.onRowClick;
136
+ const handleRowClick = onRowClick
137
+ ? (e: React.MouseEvent) => {
138
+ e.preventDefault();
139
+ onRowClick(row.original);
140
+ }
141
+ : undefined;
142
+
143
+ return (
144
+ <PCTable.Row
145
+ key={row.id}
146
+ style={onRowClick ? { cursor: 'pointer' } : {}}
147
+ onClick={handleRowClick}
148
+ >
149
+ {row.getVisibleCells().map((cell) => (
150
+ <PCTable.Cell
151
+ key={cell.id}
152
+ style={{
153
+ padding: getVar('--pc-spacing-md'),
154
+ fontSize: pcStyles.textSm.fontSize,
155
+ color: getVar('--pc-text-primary'),
156
+ borderBottom: `1px solid ${getVar('--pc-border-color')}`,
157
+ textAlign: (cell.column.columnDef.meta as TableColumnMeta | undefined)?.align || 'left',
158
+ }}
159
+ >
160
+ {flexRender(
161
+ cell.column.columnDef.cell,
162
+ cell.getContext()
163
+ )}
164
+ </PCTable.Cell>
165
+ ))}
166
+ </PCTable.Row>
167
+ );
168
+ })
169
+ )}
170
+ </PCTable.Body>
171
+ </PCTable>
172
+ </div>
173
+ );
174
+ }
@@ -0,0 +1,337 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { type Table as ReactTableType } from '@tanstack/react-table';
3
+ import {
4
+ PiCaretDoubleLeftBold,
5
+ PiCaretDoubleRightBold,
6
+ PiCaretLeftBold,
7
+ PiCaretRightBold,
8
+ } from 'react-icons/pi';
9
+ import { PCActionIcon, PCSelect, PCText } from '../ui';
10
+ import { pcStyles } from '../styles/pcStyles';
11
+
12
+ const getVar = (varName: string): string => `var(${varName})`;
13
+
14
+ const pageSizeOptions = [
15
+ { value: 5, label: '5' },
16
+ { value: 10, label: '10' },
17
+ { value: 15, label: '15' },
18
+ { value: 20, label: '20' },
19
+ { value: 25, label: '25' },
20
+ ];
21
+
22
+ export type GrievanceTablePaginationProps<TData extends Record<string, any>> = {
23
+ table: ReactTableType<TData>;
24
+ showPageSize?: boolean;
25
+ className?: string;
26
+ onPageIndexChange?: (pageIndex: number) => void;
27
+ onPageSizeChange?: (pageSize: number) => void;
28
+ };
29
+
30
+ export default function GrievanceTablePagination<
31
+ TData extends Record<string, any>,
32
+ >({
33
+ table,
34
+ showPageSize = true,
35
+ className,
36
+ onPageIndexChange,
37
+ onPageSizeChange,
38
+ }: GrievanceTablePaginationProps<TData>) {
39
+ const { t } = useTranslation();
40
+ const paginationState = table.getState().pagination;
41
+ const currentPageSize = paginationState.pageSize;
42
+ const currentPageIndex = paginationState.pageIndex;
43
+ const currentPage = currentPageIndex + 1;
44
+
45
+ // Get total rows from filtered row model (all data after filtering)
46
+ const totalRows = table.getFilteredRowModel().rows.length;
47
+ const totalPages = Math.ceil(totalRows / currentPageSize) || 0;
48
+
49
+ const currentPageSizeOption = pageSizeOptions.find(opt => opt.value === currentPageSize) || pageSizeOptions[1];
50
+
51
+ // Don't render if there's no data
52
+ if (totalRows === 0 && totalPages === 0) {
53
+ return null;
54
+ }
55
+
56
+ const canPreviousPage = currentPageIndex > 0;
57
+ const canNextPage = currentPageIndex < totalPages - 1;
58
+
59
+ const handlePageChange = (newPageIndex: number) => {
60
+ if (newPageIndex >= 0 && newPageIndex < totalPages) {
61
+ // Update via callback if provided, otherwise use table method
62
+ if (onPageIndexChange) {
63
+ onPageIndexChange(newPageIndex);
64
+ } else {
65
+ table.setPageIndex(newPageIndex);
66
+ }
67
+ }
68
+ };
69
+
70
+ const handlePageSizeChange = (newSize: number) => {
71
+ // Update via callback if provided, otherwise use table method
72
+ if (onPageSizeChange) {
73
+ onPageSizeChange(newSize);
74
+ // Reset to first page when page size changes
75
+ if (onPageIndexChange) {
76
+ onPageIndexChange(0);
77
+ } else {
78
+ table.setPageIndex(0);
79
+ }
80
+ } else {
81
+ table.setPageSize(newSize);
82
+ table.setPageIndex(0);
83
+ }
84
+ };
85
+
86
+ return (
87
+ <div
88
+ className={className}
89
+ style={{
90
+ ...pcStyles.flex,
91
+ ...pcStyles.flexCol,
92
+ ...pcStyles.itemsCenter,
93
+ ...pcStyles.justifyBetween,
94
+ width: '100%',
95
+ gap: getVar('--pc-spacing-md'),
96
+ padding: `${getVar('--pc-spacing-md')} 0`,
97
+ }}
98
+ >
99
+ {/* Desktop Layout */}
100
+ <div style={{
101
+ ...pcStyles.flex,
102
+ ...pcStyles.itemsCenter,
103
+ ...pcStyles.justifyBetween,
104
+ width: '100%',
105
+ flexWrap: 'wrap',
106
+ gap: getVar('--pc-spacing-md'),
107
+ }} className="pc-hidden-xs pc-flex-sm">
108
+ {/* Left Group: Rows per page */}
109
+ {showPageSize && (
110
+ <div style={{
111
+ ...pcStyles.flex,
112
+ ...pcStyles.itemsCenter,
113
+ ...pcStyles.gapSm,
114
+ flexShrink: 0,
115
+ }}>
116
+ <PCText
117
+ size="sm"
118
+ color="secondary"
119
+ style={{
120
+ whiteSpace: 'nowrap',
121
+ flexShrink: 0,
122
+ }}
123
+ >
124
+ {t('rowsPerPage')}
125
+ </PCText>
126
+ <PCSelect
127
+ size="sm"
128
+ options={pageSizeOptions}
129
+ value={currentPageSizeOption}
130
+ onChange={(v) => {
131
+ if (v) {
132
+ handlePageSizeChange(Number(v.value));
133
+ }
134
+ }}
135
+ style={{
136
+ width: '60px',
137
+ minWidth: '60px',
138
+ flexShrink: 0,
139
+ }}
140
+ />
141
+ </div>
142
+ )}
143
+
144
+ {/* Right Group: Page info + Navigation */}
145
+ <div style={{
146
+ ...pcStyles.flex,
147
+ ...pcStyles.itemsCenter,
148
+ ...pcStyles.gapSm,
149
+ flexShrink: 0,
150
+ }}>
151
+ <PCText size="sm" color="secondary" style={{ whiteSpace: 'nowrap' }}>
152
+ {t('pageOf', { current: currentPage, total: totalPages || 1 })}
153
+ </PCText>
154
+
155
+ <div style={{
156
+ ...pcStyles.flex,
157
+ ...pcStyles.itemsCenter,
158
+ ...pcStyles.gapXs,
159
+ }}>
160
+ <PCActionIcon
161
+ size="sm"
162
+ variant="outline"
163
+ aria-label={t('goToFirstPage')}
164
+ onClick={() => handlePageChange(0)}
165
+ disabled={!canPreviousPage}
166
+ style={{
167
+ opacity: canPreviousPage ? 1 : 0.5,
168
+ cursor: canPreviousPage ? 'pointer' : 'not-allowed',
169
+ }}
170
+ >
171
+ <PiCaretDoubleLeftBold size={16} />
172
+ </PCActionIcon>
173
+
174
+ <PCActionIcon
175
+ size="sm"
176
+ variant="outline"
177
+ aria-label={t('goToPreviousPage')}
178
+ onClick={() => handlePageChange(currentPageIndex - 1)}
179
+ disabled={!canPreviousPage}
180
+ style={{
181
+ opacity: canPreviousPage ? 1 : 0.5,
182
+ cursor: canPreviousPage ? 'pointer' : 'not-allowed',
183
+ }}
184
+ >
185
+ <PiCaretLeftBold size={16} />
186
+ </PCActionIcon>
187
+
188
+ <PCActionIcon
189
+ size="sm"
190
+ variant="outline"
191
+ aria-label={t('goToNextPage')}
192
+ onClick={() => handlePageChange(currentPageIndex + 1)}
193
+ disabled={!canNextPage}
194
+ style={{
195
+ opacity: canNextPage ? 1 : 0.5,
196
+ cursor: canNextPage ? 'pointer' : 'not-allowed',
197
+ }}
198
+ >
199
+ <PiCaretRightBold size={16} />
200
+ </PCActionIcon>
201
+
202
+ <PCActionIcon
203
+ size="sm"
204
+ variant="outline"
205
+ aria-label={t('goToLastPage')}
206
+ onClick={() => handlePageChange(totalPages - 1)}
207
+ disabled={!canNextPage}
208
+ style={{
209
+ opacity: canNextPage ? 1 : 0.5,
210
+ cursor: canNextPage ? 'pointer' : 'not-allowed',
211
+ }}
212
+ >
213
+ <PiCaretDoubleRightBold size={16} />
214
+ </PCActionIcon>
215
+ </div>
216
+ </div>
217
+ </div>
218
+
219
+ {/* Mobile Layout */}
220
+ <div style={{
221
+ ...pcStyles.flex,
222
+ ...pcStyles.flexCol,
223
+ ...pcStyles.itemsCenter,
224
+ width: '100%',
225
+ gap: getVar('--pc-spacing-sm'),
226
+ }} className="pc-flex-xs pc-hidden-sm">
227
+ {showPageSize && (
228
+ <div style={{
229
+ ...pcStyles.flex,
230
+ ...pcStyles.itemsCenter,
231
+ ...pcStyles.gapSm,
232
+ width: '100%',
233
+ justifyContent: 'center',
234
+ }}>
235
+ <PCText
236
+ size="sm"
237
+ color="secondary"
238
+ style={{
239
+ whiteSpace: 'nowrap',
240
+ }}
241
+ >
242
+ {t('rowsPerPage')}
243
+ </PCText>
244
+ <PCSelect
245
+ size="sm"
246
+ options={pageSizeOptions}
247
+ value={currentPageSizeOption}
248
+ onChange={(v) => {
249
+ if (v) {
250
+ handlePageSizeChange(Number(v.value));
251
+ }
252
+ }}
253
+ style={{
254
+ width: '60px',
255
+ minWidth: '60px',
256
+ }}
257
+ />
258
+ </div>
259
+ )}
260
+
261
+ <div style={{
262
+ ...pcStyles.flex,
263
+ ...pcStyles.itemsCenter,
264
+ ...pcStyles.justifyBetween,
265
+ width: '100%',
266
+ gap: getVar('--pc-spacing-sm'),
267
+ }}>
268
+ <PCText size="sm" color="secondary" style={{ whiteSpace: 'nowrap' }}>
269
+ {t('pageOf', { current: currentPage, total: totalPages || 1 })}
270
+ </PCText>
271
+
272
+ <div style={{
273
+ ...pcStyles.flex,
274
+ ...pcStyles.itemsCenter,
275
+ ...pcStyles.gapXs,
276
+ }}>
277
+ <PCActionIcon
278
+ size="sm"
279
+ variant="outline"
280
+ aria-label={t('goToFirstPage')}
281
+ onClick={() => handlePageChange(0)}
282
+ disabled={!canPreviousPage}
283
+ style={{
284
+ opacity: canPreviousPage ? 1 : 0.5,
285
+ cursor: canPreviousPage ? 'pointer' : 'not-allowed',
286
+ }}
287
+ >
288
+ <PiCaretDoubleLeftBold size={16} />
289
+ </PCActionIcon>
290
+
291
+ <PCActionIcon
292
+ size="sm"
293
+ variant="outline"
294
+ aria-label={t('goToPreviousPage')}
295
+ onClick={() => handlePageChange(currentPageIndex - 1)}
296
+ disabled={!canPreviousPage}
297
+ style={{
298
+ opacity: canPreviousPage ? 1 : 0.5,
299
+ cursor: canPreviousPage ? 'pointer' : 'not-allowed',
300
+ }}
301
+ >
302
+ <PiCaretLeftBold size={16} />
303
+ </PCActionIcon>
304
+
305
+ <PCActionIcon
306
+ size="sm"
307
+ variant="outline"
308
+ aria-label={t('goToNextPage')}
309
+ onClick={() => handlePageChange(currentPageIndex + 1)}
310
+ disabled={!canNextPage}
311
+ style={{
312
+ opacity: canNextPage ? 1 : 0.5,
313
+ cursor: canNextPage ? 'pointer' : 'not-allowed',
314
+ }}
315
+ >
316
+ <PiCaretRightBold size={16} />
317
+ </PCActionIcon>
318
+
319
+ <PCActionIcon
320
+ size="sm"
321
+ variant="outline"
322
+ aria-label={t('goToLastPage')}
323
+ onClick={() => handlePageChange(totalPages - 1)}
324
+ disabled={!canNextPage}
325
+ style={{
326
+ opacity: canNextPage ? 1 : 0.5,
327
+ cursor: canNextPage ? 'pointer' : 'not-allowed',
328
+ }}
329
+ >
330
+ <PiCaretDoubleRightBold size={16} />
331
+ </PCActionIcon>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ );
337
+ }