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