@open-mercato/core 0.6.8-develop.7029.1.a1bb3363af → 0.6.8-develop.7031.1.005201cd70

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 (236) hide show
  1. package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
  2. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  3. package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
  4. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  5. package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
  6. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  7. package/dist/modules/auth/backend/roles/page.js +3 -1
  8. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  9. package/dist/modules/auth/backend/users/page.js +2 -1
  10. package/dist/modules/auth/backend/users/page.js.map +2 -2
  11. package/dist/modules/business_rules/backend/logs/page.js +3 -1
  12. package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
  13. package/dist/modules/business_rules/backend/rules/page.js +3 -1
  14. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  15. package/dist/modules/business_rules/backend/sets/page.js +3 -1
  16. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  17. package/dist/modules/catalog/ai-agents.js +1 -1
  18. package/dist/modules/catalog/ai-agents.js.map +1 -1
  19. package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
  20. package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
  21. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
  22. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  23. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
  24. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  25. package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
  26. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  27. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
  28. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
  29. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
  30. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  31. package/dist/modules/configs/lib/system-status.js +9 -0
  32. package/dist/modules/configs/lib/system-status.js.map +2 -2
  33. package/dist/modules/currencies/backend/currencies/page.js +3 -1
  34. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  35. package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
  36. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  37. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
  38. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  39. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +3 -1
  40. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  41. package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
  42. package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
  43. package/dist/modules/customers/ai-tools/deals-pack.js +4 -1
  44. package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
  45. package/dist/modules/customers/ai-tools/people-pack.js +4 -1
  46. package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
  47. package/dist/modules/customers/api/deals/map/route.js +2 -1
  48. package/dist/modules/customers/api/deals/map/route.js.map +2 -2
  49. package/dist/modules/customers/backend/customers/companies/page.js +3 -1
  50. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  51. package/dist/modules/customers/backend/customers/deals/page.js +5 -2
  52. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  53. package/dist/modules/customers/backend/customers/people/page.js +3 -1
  54. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  55. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  56. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  57. package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
  58. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  59. package/dist/modules/devices/backend/devices/page.js +3 -1
  60. package/dist/modules/devices/backend/devices/page.js.map +2 -2
  61. package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
  62. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  63. package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
  64. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  65. package/dist/modules/entities/api/records.js +4 -2
  66. package/dist/modules/entities/api/records.js.map +2 -2
  67. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
  68. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  69. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
  70. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
  71. package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
  72. package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
  73. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
  74. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
  75. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
  76. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
  77. package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
  78. package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
  79. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
  80. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  81. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
  82. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  83. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
  84. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
  85. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
  86. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  87. package/dist/modules/messages/components/MessagesInboxPageClient.js +4 -1
  88. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  89. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
  90. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  91. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
  92. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  93. package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
  94. package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
  95. package/dist/modules/query_index/lib/engine.js +108 -48
  96. package/dist/modules/query_index/lib/engine.js.map +2 -2
  97. package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
  98. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  99. package/dist/modules/resources/backend/resources/resources/page.js +3 -1
  100. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  101. package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
  102. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  103. package/dist/modules/sales/backend/sales/channels/page.js +3 -0
  104. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  105. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
  106. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  107. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +4 -0
  108. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  109. package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
  110. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  111. package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
  112. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  113. package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
  114. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  115. package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
  116. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  117. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
  118. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  119. package/dist/modules/staff/backend/staff/teams/page.js +3 -0
  120. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  121. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
  122. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  123. package/dist/modules/warranty_claims/backend/page.js +3 -0
  124. package/dist/modules/warranty_claims/backend/page.js.map +2 -2
  125. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
  126. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
  127. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
  128. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
  129. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
  130. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
  131. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
  132. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
  133. package/dist/modules/wms/commands/inventory-actions.js +3 -0
  134. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  135. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +4 -0
  136. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  137. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
  138. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  139. package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
  140. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  141. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
  142. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
  143. package/dist/modules/workflows/backend/definitions/page.js +3 -1
  144. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  145. package/dist/modules/workflows/backend/events/page.js +3 -1
  146. package/dist/modules/workflows/backend/events/page.js.map +2 -2
  147. package/dist/modules/workflows/backend/instances/page.js +3 -1
  148. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  149. package/dist/modules/workflows/backend/tasks/page.js +3 -1
  150. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  151. package/package.json +7 -7
  152. package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
  153. package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
  154. package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
  155. package/src/modules/auth/backend/roles/page.tsx +4 -2
  156. package/src/modules/auth/backend/users/page.tsx +3 -2
  157. package/src/modules/business_rules/backend/logs/page.tsx +4 -1
  158. package/src/modules/business_rules/backend/rules/page.tsx +4 -1
  159. package/src/modules/business_rules/backend/sets/page.tsx +4 -1
  160. package/src/modules/catalog/ai-agents.ts +1 -1
  161. package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
  162. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
  163. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
  164. package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
  165. package/src/modules/catalog/i18n/de.json +1 -0
  166. package/src/modules/catalog/i18n/en.json +1 -0
  167. package/src/modules/catalog/i18n/es.json +1 -0
  168. package/src/modules/catalog/i18n/ko.json +1 -0
  169. package/src/modules/catalog/i18n/pl.json +1 -0
  170. package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
  171. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
  172. package/src/modules/configs/i18n/de.json +2 -0
  173. package/src/modules/configs/i18n/en.json +2 -0
  174. package/src/modules/configs/i18n/es.json +2 -0
  175. package/src/modules/configs/i18n/ko.json +2 -0
  176. package/src/modules/configs/i18n/pl.json +2 -0
  177. package/src/modules/configs/lib/system-status.ts +9 -0
  178. package/src/modules/currencies/backend/currencies/page.tsx +4 -1
  179. package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
  180. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
  181. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +4 -1
  182. package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
  183. package/src/modules/customers/ai-tools/deals-pack.ts +4 -1
  184. package/src/modules/customers/ai-tools/people-pack.ts +4 -1
  185. package/src/modules/customers/api/deals/map/route.ts +1 -0
  186. package/src/modules/customers/backend/customers/companies/page.tsx +4 -1
  187. package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
  188. package/src/modules/customers/backend/customers/people/page.tsx +4 -1
  189. package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
  190. package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
  191. package/src/modules/devices/backend/devices/page.tsx +4 -1
  192. package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
  193. package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
  194. package/src/modules/entities/api/records.ts +2 -0
  195. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
  196. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
  197. package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
  198. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
  199. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
  200. package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
  201. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
  202. package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
  203. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
  204. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
  205. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
  206. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
  207. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
  208. package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
  209. package/src/modules/query_index/lib/engine.ts +156 -53
  210. package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
  211. package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
  212. package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
  213. package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
  214. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
  215. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +5 -0
  216. package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
  217. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
  218. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
  219. package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
  220. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
  221. package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
  222. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
  223. package/src/modules/warranty_claims/backend/page.tsx +4 -0
  224. package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
  225. package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
  226. package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
  227. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
  228. package/src/modules/wms/commands/inventory-actions.ts +3 -0
  229. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +5 -0
  230. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
  231. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
  232. package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
  233. package/src/modules/workflows/backend/definitions/page.tsx +4 -1
  234. package/src/modules/workflows/backend/events/page.tsx +4 -1
  235. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  236. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
@@ -39,6 +39,7 @@ type CategoriesResponse = {
39
39
  page: number
40
40
  pageSize: number
41
41
  totalPages: number
42
+ totalIsCapped?: boolean
42
43
  }
43
44
 
44
45
  const PAGE_SIZE = 50
@@ -114,6 +115,7 @@ export default function CategoriesDataTable() {
114
115
  const rows = data?.items ?? []
115
116
  const total = data?.total ?? 0
116
117
  const totalPages = data?.totalPages ?? 0
118
+ const totalIsCapped = data?.totalIsCapped === true
117
119
 
118
120
  const columns = React.useMemo<ColumnDef<CategoryRow>[]>(() => [
119
121
  {
@@ -249,6 +251,7 @@ export default function CategoriesDataTable() {
249
251
  pageSize: PAGE_SIZE,
250
252
  total,
251
253
  totalPages,
254
+ totalIsCapped,
252
255
  onPageChange: setPage,
253
256
  }}
254
257
  isLoading={isLoading}
@@ -86,6 +86,7 @@ type ProductsResponse = {
86
86
  items?: ProductRow[]
87
87
  total?: number
88
88
  totalPages?: number
89
+ totalIsCapped?: boolean
89
90
  }
90
91
 
91
92
  const PAGE_SIZE = 25
@@ -177,6 +178,7 @@ export default function ProductsDataTable({
177
178
  const [page, setPage] = React.useState(1)
178
179
  const [total, setTotal] = React.useState(0)
179
180
  const [totalPages, setTotalPages] = React.useState(1)
181
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
180
182
  const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)
181
183
  const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])
182
184
  const [search, setSearch] = React.useState('')
@@ -598,6 +600,7 @@ export default function ProductsDataTable({
598
600
  setRows(normalized)
599
601
  setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)
600
602
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
603
+ setTotalIsCapped(payload.totalIsCapped === true)
601
604
  } catch (error) {
602
605
  if (!cancelled) {
603
606
  setCacheStatus(null)
@@ -712,12 +715,16 @@ export default function ProductsDataTable({
712
715
  // dependency on the host page.
713
716
  total,
714
717
  totalMatching: total,
718
+ // `total` is a floor when the server capped the count, so the
719
+ // merchandising widget must not present it as an exact match count.
720
+ totalIsCapped,
715
721
  }}
716
722
  pagination={{
717
723
  page,
718
724
  pageSize: PAGE_SIZE,
719
725
  total,
720
726
  totalPages,
727
+ totalIsCapped,
721
728
  onPageChange: setPage,
722
729
  cacheStatus,
723
730
  }}
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Merchandising-Assistent",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} Produkte in der Ansicht",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ Produkte in der Ansicht",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} Produkte ausgewählt",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Merchandising Assistant",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} products in view",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ products in view",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} products selected",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Asistente de Merchandising",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} productos en la vista",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ productos en la vista",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} productos seleccionados",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "머천다이징 어시스턴트",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "카테고리로 필터링됨",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} 제품 보기",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "조회 중 {count}+개 제품",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} 제품 선택됨",
94
95
  "catalog.merchandising_assistant.context.tags": "태그 {count}개",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "카탈로그",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Asystent merchandisingu",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtrowane po kategorii",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} produktów w widoku",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ produktów w widoku",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "Wybrano {count} produktów",
94
95
  "catalog.merchandising_assistant.context.tags": "Tagi: {count}",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Katalog",
@@ -31,6 +31,8 @@ interface HostInjectionContext {
31
31
  scopeVersion?: unknown
32
32
  total?: number | string
33
33
  totalMatching?: number | string
34
+ /** `totalMatching` is a floor, not an exact count (server-capped list count). */
35
+ totalIsCapped?: boolean
34
36
  /** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */
35
37
  selectedRowIds?: string[]
36
38
  selectedCount?: number
@@ -83,6 +85,7 @@ export function computeCatalogMerchandisingPageContext(
83
85
  context: HostInjectionContext | undefined,
84
86
  ): MerchandisingPageContext {
85
87
  const totalMatching = readNumber(context?.totalMatching ?? context?.total)
88
+ const totalMatchingIsCapped = context?.totalIsCapped === true
86
89
  const selectedRowIds = Array.isArray(context?.selectedRowIds) ? context.selectedRowIds : []
87
90
  const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.selectedCount)
88
91
  return {
@@ -93,6 +96,7 @@ export function computeCatalogMerchandisingPageContext(
93
96
  extra: {
94
97
  filter: normalizeFilters(context),
95
98
  totalMatching,
99
+ totalMatchingIsCapped,
96
100
  selectedCount,
97
101
  },
98
102
  }
@@ -24,6 +24,7 @@ type ChannelListResponse = {
24
24
  items?: ChannelRow[]
25
25
  total?: number
26
26
  totalPages?: number
27
+ totalIsCapped?: boolean
27
28
  }
28
29
 
29
30
  export default function ChannelsListPage() {
@@ -31,6 +32,7 @@ export default function ChannelsListPage() {
31
32
  const [rows, setRows] = React.useState<ChannelRow[]>([])
32
33
  const [total, setTotal] = React.useState(0)
33
34
  const [totalPages, setTotalPages] = React.useState(1)
35
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
34
36
  const [page, setPage] = React.useState(1)
35
37
  const [isLoading, setIsLoading] = React.useState(true)
36
38
  const [errorMessage, setErrorMessage] = React.useState<string | null>(null)
@@ -72,6 +74,7 @@ export default function ChannelsListPage() {
72
74
  setRows(Array.isArray(data.items) ? data.items : [])
73
75
  setTotal(typeof data.total === 'number' ? data.total : 0)
74
76
  setTotalPages(typeof data.totalPages === 'number' ? data.totalPages : 1)
77
+ setTotalIsCapped(data.totalIsCapped === true)
75
78
  }
76
79
  setIsLoading(false)
77
80
  }
@@ -139,6 +142,7 @@ export default function ChannelsListPage() {
139
142
  pageSize,
140
143
  total,
141
144
  totalPages,
145
+ totalIsCapped,
142
146
  onPageChange: setPage,
143
147
  }}
144
148
  />
@@ -155,6 +155,8 @@
155
155
  "configs.systemStatus.variables.entitiesBackcompatEav.label": "Legacy-EAV-Schreibvorgänge",
156
156
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.description": "Query-Index-Ergebnisse auch bei unvollständiger Abdeckung bereitstellen.",
157
157
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.label": "Teilindex-Nutzung erzwingen",
158
+ "configs.systemStatus.variables.listCountCap.description": "Maximal gemeldete Gesamtzahl einer Liste; die Zählung stoppt an der Obergrenze und begrenzte Summen werden z. B. als „10 000+“ angezeigt. 0 deaktiviert die Begrenzung.",
159
+ "configs.systemStatus.variables.listCountCap.label": "Obergrenze für Listenzählung",
158
160
  "configs.systemStatus.variables.logLevel.description": "Fallback-Log-Level, wenn die Verbosität nicht gesetzt ist.",
159
161
  "configs.systemStatus.variables.logLevel.label": "Log-Level",
160
162
  "configs.systemStatus.variables.logVerbosity.description": "Überschreibt die strukturierte Log-Verbosität wie Debug oder Trace.",
@@ -155,6 +155,8 @@
155
155
  "configs.systemStatus.variables.entitiesBackcompatEav.label": "Legacy EAV writes",
156
156
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.description": "Serve query index results even when coverage is partial.",
157
157
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.label": "Force partial index usage",
158
+ "configs.systemStatus.variables.listCountCap.description": "Maximum reported list total; counting stops at the cap and capped totals render as e.g. “10 000+”. 0 disables capping.",
159
+ "configs.systemStatus.variables.listCountCap.label": "List count cap",
158
160
  "configs.systemStatus.variables.logLevel.description": "Fallback log level applied when verbosity is not set.",
159
161
  "configs.systemStatus.variables.logLevel.label": "Log level",
160
162
  "configs.systemStatus.variables.logVerbosity.description": "Overrides structured log verbosity such as debug or trace.",
@@ -155,6 +155,8 @@
155
155
  "configs.systemStatus.variables.entitiesBackcompatEav.label": "Escrituras EAV heredadas",
156
156
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.description": "Servir resultados del índice de consultas incluso cuando la cobertura es parcial.",
157
157
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.label": "Forzar uso de índice parcial",
158
+ "configs.systemStatus.variables.listCountCap.description": "Total máximo informado en listas; el conteo se detiene en el límite y los totales limitados se muestran p. ej. como «10 000+». 0 desactiva el límite.",
159
+ "configs.systemStatus.variables.listCountCap.label": "Límite del conteo de listas",
158
160
  "configs.systemStatus.variables.logLevel.description": "Nivel de registro de respaldo cuando la verbosidad no está configurada.",
159
161
  "configs.systemStatus.variables.logLevel.label": "Nivel de registro",
160
162
  "configs.systemStatus.variables.logVerbosity.description": "Anula la verbosidad del registro estructurado, como debug o trace.",
@@ -155,6 +155,8 @@
155
155
  "configs.systemStatus.variables.entitiesBackcompatEav.label": "레거시 EAV 쓰기",
156
156
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.description": "커버리지가 부분적이어도 쿼리 인덱스 결과를 제공합니다.",
157
157
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.label": "부분 인덱스 사용 강제",
158
+ "configs.systemStatus.variables.listCountCap.description": "목록 총계의 최대 보고 값입니다. 카운트는 상한에서 멈추며 제한된 총계는 예를 들어 “10 000+”로 표시됩니다. 0이면 상한이 비활성화됩니다.",
159
+ "configs.systemStatus.variables.listCountCap.label": "목록 카운트 상한",
158
160
  "configs.systemStatus.variables.logLevel.description": "상세 수준이 설정되지 않았을 때 적용되는 대체 로그 레벨입니다.",
159
161
  "configs.systemStatus.variables.logLevel.label": "로그 레벨",
160
162
  "configs.systemStatus.variables.logVerbosity.description": "debug나 trace와 같은 구조화된 로그 상세 수준을 재정의합니다.",
@@ -155,6 +155,8 @@
155
155
  "configs.systemStatus.variables.entitiesBackcompatEav.label": "Zapis do legacy EAV",
156
156
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.description": "Pozwala korzystać z wyników indeksu zapytań nawet przy częściowym pokryciu.",
157
157
  "configs.systemStatus.variables.forceQueryIndexOnPartialIndexes.label": "Wymuś użycie częściowego indeksu",
158
+ "configs.systemStatus.variables.listCountCap.description": "Maksymalna raportowana liczba wyników listy; zliczanie zatrzymuje się na limicie, a ograniczone sumy są pokazywane np. jako „10 000+”. 0 wyłącza limit.",
159
+ "configs.systemStatus.variables.listCountCap.label": "Limit licznika list",
158
160
  "configs.systemStatus.variables.logLevel.description": "Domyślny poziom logowania używany, gdy nie ustawiono szczegółowości.",
159
161
  "configs.systemStatus.variables.logLevel.label": "Poziom logowania",
160
162
  "configs.systemStatus.variables.logVerbosity.description": "Nadpisuje poziom szczegółowości logów, np. debug lub trace.",
@@ -267,6 +267,15 @@ export const SYSTEM_STATUS_VARIABLES: SystemStatusVariableDefinition[] = [
267
267
  docUrl: `${SYSTEM_STATUS_DOC_BASE}#optimize_index_coverage_stats`,
268
268
  defaultValue: 'false',
269
269
  },
270
+ {
271
+ key: 'OM_LIST_COUNT_CAP',
272
+ category: 'query_index',
273
+ kind: 'string',
274
+ labelKey: 'configs.systemStatus.variables.listCountCap.label',
275
+ descriptionKey: 'configs.systemStatus.variables.listCountCap.description',
276
+ docUrl: `${SYSTEM_STATUS_DOC_BASE}#om_list_count_cap`,
277
+ defaultValue: '10000',
278
+ },
270
279
  {
271
280
  key: 'FORCE_QUERY_INDEX_ON_PARTIAL_INDEXES',
272
281
  category: 'query_index',
@@ -41,6 +41,7 @@ type ResponsePayload = {
41
41
  total: number
42
42
  page: number
43
43
  totalPages: number
44
+ totalIsCapped?: boolean
44
45
  }
45
46
 
46
47
  export default function CurrenciesPage() {
@@ -50,6 +51,7 @@ export default function CurrenciesPage() {
50
51
  const [page, setPage] = React.useState(1)
51
52
  const [total, setTotal] = React.useState(0)
52
53
  const [totalPages, setTotalPages] = React.useState(1)
54
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
53
55
  const [search, setSearch] = React.useState('')
54
56
  const [filters, setFilters] = React.useState<FilterValues>({})
55
57
  const [isLoading, setIsLoading] = React.useState(true)
@@ -96,6 +98,7 @@ export default function CurrenciesPage() {
96
98
  setRows(Array.isArray(payload.items) ? payload.items : [])
97
99
  setTotal(payload.total || 0)
98
100
  setTotalPages(payload.totalPages || 1)
101
+ setTotalIsCapped(payload?.totalIsCapped === true)
99
102
  }
100
103
  } catch (error) {
101
104
  if (!cancelled) {
@@ -342,7 +345,7 @@ export default function CurrenciesPage() {
342
345
  createLabel={t('currencies.list.actions.create')}
343
346
  />
344
347
  )}
345
- pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
348
+ pagination={{ page, pageSize: 50, total, totalPages, totalIsCapped, onPageChange: setPage }}
346
349
  isLoading={isLoading}
347
350
  perspective={{ tableId: extensionPoints.hosts.currenciesTable.tableId }}
348
351
  />
@@ -42,6 +42,7 @@ type ResponsePayload = {
42
42
  total: number
43
43
  page: number
44
44
  totalPages: number
45
+ totalIsCapped?: boolean
45
46
  }
46
47
 
47
48
  export default function ExchangeRatesPage() {
@@ -51,6 +52,7 @@ export default function ExchangeRatesPage() {
51
52
  const [page, setPage] = React.useState(1)
52
53
  const [total, setTotal] = React.useState(0)
53
54
  const [totalPages, setTotalPages] = React.useState(1)
55
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
54
56
  const [search, setSearch] = React.useState('')
55
57
  const [filters, setFilters] = React.useState<FilterValues>({})
56
58
  const [isLoading, setIsLoading] = React.useState(true)
@@ -100,6 +102,7 @@ export default function ExchangeRatesPage() {
100
102
  setRows(Array.isArray(payload.items) ? payload.items : [])
101
103
  setTotal(payload.total || 0)
102
104
  setTotalPages(payload.totalPages || 1)
105
+ setTotalIsCapped(payload?.totalIsCapped === true)
103
106
  }
104
107
  } catch (error) {
105
108
  if (!cancelled) {
@@ -332,7 +335,7 @@ export default function ExchangeRatesPage() {
332
335
  createLabel={t('exchangeRates.list.actions.create')}
333
336
  />
334
337
  )}
335
- pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
338
+ pagination={{ page, pageSize: 50, total, totalPages, totalIsCapped, onPageChange: setPage }}
336
339
  isLoading={isLoading}
337
340
  perspective={{ tableId: extensionPoints.hosts.exchangeRatesTable.tableId }}
338
341
  />
@@ -34,6 +34,7 @@ type RolesResponse = {
34
34
  items?: RoleRow[]
35
35
  total?: number
36
36
  totalPages?: number
37
+ totalIsCapped?: boolean
37
38
  }
38
39
 
39
40
  export default function CustomerRolesPage() {
@@ -45,6 +46,7 @@ export default function CustomerRolesPage() {
45
46
  const [pageSize] = React.useState(50)
46
47
  const [total, setTotal] = React.useState(0)
47
48
  const [totalPages, setTotalPages] = React.useState(1)
49
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
48
50
  const [search, setSearch] = React.useState('')
49
51
  const [isLoading, setIsLoading] = React.useState(true)
50
52
  const [reloadToken, setReloadToken] = React.useState(0)
@@ -88,6 +90,7 @@ export default function CustomerRolesPage() {
88
90
  setRows(items)
89
91
  setTotal(typeof payload?.total === 'number' ? payload.total : items.length)
90
92
  setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)
93
+ setTotalIsCapped(payload?.totalIsCapped === true)
91
94
  } catch (err) {
92
95
  if (!cancelled) {
93
96
  const message = err instanceof Error ? err.message : t('customer_accounts.admin.roles.error.load', 'Failed to load roles')
@@ -240,7 +243,7 @@ export default function CustomerRolesPage() {
240
243
  ]}
241
244
  />
242
245
  )}
243
- pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}
246
+ pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}
244
247
  isLoading={isLoading}
245
248
  />
246
249
  </PageBody>
@@ -41,6 +41,7 @@ type UsersResponse = {
41
41
  items?: UserRow[]
42
42
  total?: number
43
43
  totalPages?: number
44
+ totalIsCapped?: boolean
44
45
  }
45
46
 
46
47
  function formatDate(value: string | null | undefined, fallback: string): string {
@@ -235,6 +236,7 @@ export default function CustomerAccountsPage() {
235
236
  const [pageSize] = React.useState(50)
236
237
  const [total, setTotal] = React.useState(0)
237
238
  const [totalPages, setTotalPages] = React.useState(1)
239
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
238
240
  const [search, setSearch] = React.useState('')
239
241
  const [filterValues, setFilterValues] = React.useState<FilterValues>({})
240
242
  const [isLoading, setIsLoading] = React.useState(true)
@@ -314,6 +316,7 @@ export default function CustomerAccountsPage() {
314
316
  setRows(items)
315
317
  setTotal(typeof payload?.total === 'number' ? payload.total : items.length)
316
318
  setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)
319
+ setTotalIsCapped(payload?.totalIsCapped === true)
317
320
  } catch (err) {
318
321
  if (!cancelled) {
319
322
  const message = err instanceof Error ? err.message : t('customer_accounts.admin.error.loadUsers', 'Failed to load customer users')
@@ -579,7 +582,7 @@ export default function CustomerAccountsPage() {
579
582
  ]}
580
583
  />
581
584
  )}
582
- pagination={{ page, pageSize, total, totalPages, onPageChange: setPage }}
585
+ pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage }}
583
586
  isLoading={isLoading}
584
587
  />
585
588
  <CreateUserDialog
@@ -85,7 +85,7 @@ const listCompaniesTool = defineApiBackedAiTool<
85
85
  name: 'customers.list_companies',
86
86
  displayName: 'List companies',
87
87
  description:
88
- 'Search / list companies for the caller tenant + organization. Returns { items, total, limit, offset }.',
88
+ 'Search / list companies for the caller tenant + organization. Returns { items, total, totalIsCapped, limit, offset }. When totalIsCapped is true, total is a floor ("at least N", render it as "N+"), and pagination is exhausted only when a page returns fewer than limit items — never when offset reaches total.',
89
89
  inputSchema: listCompaniesInput,
90
90
  requiredFeatures: ['customers.companies.view'],
91
91
  toOperation: (input, ctx) => {
@@ -122,6 +122,9 @@ const listCompaniesTool = defineApiBackedAiTool<
122
122
  sizeBucket: row.size_bucket ?? row.sizeBucket ?? null,
123
123
  })),
124
124
  total: typeof data.total === 'number' ? data.total : 0,
125
+ // A capped count reports a floor: phrase the total as "at least N" and
126
+ // never treat it as proof that pagination is exhausted.
127
+ totalIsCapped: (data as { totalIsCapped?: boolean }).totalIsCapped === true,
125
128
  limit,
126
129
  offset,
127
130
  }
@@ -99,7 +99,7 @@ const listDealsTool = defineApiBackedAiTool<ListDealsInput, ListDealsApiResponse
99
99
  name: 'customers.list_deals',
100
100
  displayName: 'List deals',
101
101
  description:
102
- 'Search / list deals for the caller tenant + organization. Optional filters include linked person / company / pipeline stage.',
102
+ 'Search / list deals for the caller tenant + organization. Optional filters include linked person / company / pipeline stage. Returns { items, total, totalIsCapped, limit, offset }. When totalIsCapped is true, total is a floor ("at least N", render it as "N+"), and pagination is exhausted only when a page returns fewer than limit items — never when offset reaches total.',
103
103
  inputSchema: listDealsInput,
104
104
  requiredFeatures: ['customers.deals.view'],
105
105
  toOperation: (input, ctx) => {
@@ -155,6 +155,9 @@ const listDealsTool = defineApiBackedAiTool<ListDealsInput, ListDealsApiResponse
155
155
  }
156
156
  }),
157
157
  total: typeof data.total === 'number' ? data.total : 0,
158
+ // A capped count reports a floor: phrase the total as "at least N" and
159
+ // never treat it as proof that pagination is exhausted.
160
+ totalIsCapped: (data as { totalIsCapped?: boolean }).totalIsCapped === true,
158
161
  limit,
159
162
  offset,
160
163
  }
@@ -90,7 +90,7 @@ const listPeopleTool = defineApiBackedAiTool<ListPeopleInput, ListPeopleApiRespo
90
90
  name: 'customers.list_people',
91
91
  displayName: 'List people',
92
92
  description:
93
- 'Search / list people (CRM persons) for the caller tenant + organization. Returns { items, total, limit, offset }.',
93
+ 'Search / list people (CRM persons) for the caller tenant + organization. Returns { items, total, totalIsCapped, limit, offset }. When totalIsCapped is true, total is a floor ("at least N", render it as "N+"), and pagination is exhausted only when a page returns fewer than limit items — never when offset reaches total.',
94
94
  inputSchema: listPeopleInput,
95
95
  requiredFeatures: ['customers.people.view'],
96
96
  toOperation: async (input, ctx) => {
@@ -144,6 +144,9 @@ const listPeopleTool = defineApiBackedAiTool<ListPeopleInput, ListPeopleApiRespo
144
144
  return {
145
145
  items: rawItems.map((row) => toCustomerListSummary(row)),
146
146
  total: typeof data.total === 'number' ? data.total : 0,
147
+ // A capped count reports a floor: phrase the total as "at least N" and
148
+ // never treat it as proof that pagination is exhausted.
149
+ totalIsCapped: (data as { totalIsCapped?: boolean }).totalIsCapped === true,
147
150
  limit,
148
151
  offset,
149
152
  }
@@ -545,5 +545,6 @@ export async function GET(req: Request) {
545
545
  page: query.page,
546
546
  pageSize: query.pageSize,
547
547
  totalPages: Math.ceil(res.total / (query.pageSize || 1)),
548
+ ...(res.meta?.listCountCapWarning ? { totalIsCapped: true } : {}),
548
549
  })
549
550
  }
@@ -118,6 +118,7 @@ type CompaniesResponse = {
118
118
  total?: number
119
119
  page?: number
120
120
  totalPages?: number
121
+ totalIsCapped?: boolean
121
122
  }
122
123
 
123
124
  type DictionaryKindKey = CustomerDictionaryKind
@@ -198,6 +199,7 @@ export default function CustomersCompaniesPage() {
198
199
  const [sorting, setSorting] = React.useState<SortingState>([])
199
200
  const [total, setTotal] = React.useState(0)
200
201
  const [totalPages, setTotalPages] = React.useState(1)
202
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
201
203
  const [search, setSearch] = React.useState('')
202
204
  const pathname = usePathname()
203
205
  const searchParams = useSearchParams()
@@ -422,6 +424,7 @@ export default function CustomersCompaniesPage() {
422
424
  setRows(items.map((item) => mapApiItem(item as Record<string, unknown>)).filter((row): row is CompanyRow => !!row))
423
425
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
424
426
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
427
+ setTotalIsCapped(payload?.totalIsCapped === true)
425
428
  } catch (err) {
426
429
  if (!cancelled) {
427
430
  setCacheStatus(null)
@@ -963,7 +966,7 @@ export default function CustomersCompaniesPage() {
963
966
  />
964
967
  )}
965
968
  virtualized
966
- pagination={{ page, pageSize, total, totalPages, onPageChange: setPage, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange, cacheStatus }}
969
+ pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange, cacheStatus }}
967
970
  isLoading={isLoading}
968
971
  />
969
972
  <AdvancedFilterPanel
@@ -125,6 +125,7 @@ type DealsResponse = {
125
125
  items?: Array<Record<string, unknown>>
126
126
  total?: number
127
127
  totalPages?: number
128
+ totalIsCapped?: boolean
128
129
  }
129
130
 
130
131
  type FilterOption = { value: string; label: string }
@@ -210,6 +211,7 @@ export default function CustomersDealsPage() {
210
211
  const [sorting, setSorting] = React.useState<import('@tanstack/react-table').SortingState>([])
211
212
  const [total, setTotal] = React.useState(0)
212
213
  const [totalPages, setTotalPages] = React.useState(1)
214
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
213
215
  const [search, setSearch] = React.useState(() => searchParams?.get('search')?.trim() ?? '')
214
216
  const [isLoading, setIsLoading] = React.useState(false)
215
217
  const [reloadToken, setReloadToken] = React.useState(0)
@@ -397,6 +399,7 @@ export default function CustomersDealsPage() {
397
399
  setRows(mapped)
398
400
  setTotal(typeof payload.total === 'number' ? payload.total : mapped.length)
399
401
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
402
+ setTotalIsCapped(payload.totalIsCapped === true)
400
403
  } catch (err) {
401
404
  if (!cancelled) {
402
405
  setCacheStatus(null)
@@ -412,10 +415,12 @@ export default function CustomersDealsPage() {
412
415
  }, [queryParams, reloadToken, scopeVersion, t])
413
416
 
414
417
  React.useEffect(() => {
415
- if (totalPages > 0 && page > totalPages) {
418
+ // A capped totalPages is a floor pages past it hold reachable rows, so
419
+ // clamping would bounce a deep-linked user off data that exists.
420
+ if (!totalIsCapped && totalPages > 0 && page > totalPages) {
416
421
  setPage(totalPages)
417
422
  }
418
- }, [page, totalPages])
423
+ }, [page, totalPages, totalIsCapped])
419
424
 
420
425
  const queryRef = React.useRef(searchParams?.toString() ?? '')
421
426
  React.useEffect(() => {
@@ -1136,6 +1141,7 @@ export default function CustomersDealsPage() {
1136
1141
  pageSize,
1137
1142
  total,
1138
1143
  totalPages,
1144
+ totalIsCapped,
1139
1145
  onPageChange: (nextPage) => setPage(nextPage),
1140
1146
  pageSizeOptions: [10, 25, 50, 100],
1141
1147
  onPageSizeChange: handlePageSizeChange,
@@ -125,6 +125,7 @@ type PeopleResponse = {
125
125
  total?: number
126
126
  page?: number
127
127
  totalPages?: number
128
+ totalIsCapped?: boolean
128
129
  }
129
130
 
130
131
  type DictionaryKindKey = CustomerDictionaryKind
@@ -206,6 +207,7 @@ export default function CustomersPeoplePage() {
206
207
  const [sorting, setSorting] = React.useState<SortingState>([])
207
208
  const [total, setTotal] = React.useState(0)
208
209
  const [totalPages, setTotalPages] = React.useState(1)
210
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
209
211
  const [search, setSearch] = React.useState('')
210
212
  const pathname = usePathname()
211
213
  const searchParams = useSearchParams()
@@ -435,6 +437,7 @@ export default function CustomersPeoplePage() {
435
437
  setRows(items.map((item) => mapApiItem(item as Record<string, unknown>)).filter((row): row is PersonRow => !!row))
436
438
  setTotal(typeof payload.total === 'number' ? payload.total : items.length)
437
439
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
440
+ setTotalIsCapped(payload?.totalIsCapped === true)
438
441
  } catch (err) {
439
442
  if (!cancelled) {
440
443
  setCacheStatus(null)
@@ -990,7 +993,7 @@ export default function CustomersPeoplePage() {
990
993
  />
991
994
  )}
992
995
  virtualized
993
- pagination={{ page, pageSize, total, totalPages, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange }}
996
+ pagination={{ page, pageSize, total, totalPages, totalIsCapped, onPageChange: setPage, cacheStatus, pageSizeOptions: [10, 25, 50, 100], onPageSizeChange: handlePageSizeChange }}
994
997
  isLoading={isLoading}
995
998
  />
996
999
  <AdvancedFilterPanel
@@ -47,6 +47,7 @@ type CustomerTodosResponse = {
47
47
  page: number
48
48
  pageSize: number
49
49
  totalPages: number
50
+ totalIsCapped?: boolean
50
51
  }
51
52
 
52
53
  const TASKS_TAB_QUERY = 'tab=tasks'
@@ -279,6 +280,7 @@ export function CustomerTodosTable(): React.JSX.Element {
279
280
  pageSize,
280
281
  total: data?.total ?? 0,
281
282
  totalPages: data?.totalPages ?? 0,
283
+ totalIsCapped: data?.totalIsCapped === true,
282
284
  onPageChange: setPage,
283
285
  }}
284
286
  isLoading={isLoading}
@@ -74,6 +74,7 @@ type ResponsePayload = {
74
74
  total: number
75
75
  page: number
76
76
  totalPages: number
77
+ totalIsCapped?: boolean
77
78
  }
78
79
 
79
80
  type SyncOption = {
@@ -153,6 +154,7 @@ export default function SyncRunsDashboardPage() {
153
154
  const [page, setPage] = React.useState(1)
154
155
  const [total, setTotal] = React.useState(0)
155
156
  const [totalPages, setTotalPages] = React.useState(1)
157
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
156
158
  const [search, setSearch] = React.useState('')
157
159
  const [filterValues, setFilterValues] = React.useState<FilterValues>({})
158
160
  const [isLoading, setIsLoading] = React.useState(true)
@@ -200,6 +202,7 @@ export default function SyncRunsDashboardPage() {
200
202
  setRows(Array.isArray(payload.items) ? payload.items : [])
201
203
  setTotal(payload.total || 0)
202
204
  setTotalPages(payload.totalPages || 1)
205
+ setTotalIsCapped(payload?.totalIsCapped === true)
203
206
  setIsLoading(false)
204
207
  }
205
208
  }
@@ -1065,7 +1068,7 @@ export default function SyncRunsDashboardPage() {
1065
1068
  }] : []),
1066
1069
  ]} />
1067
1070
  )}
1068
- pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}
1071
+ pagination={{ page, pageSize: 20, total, totalPages, totalIsCapped, onPageChange: setPage }}
1069
1072
  isLoading={isLoading}
1070
1073
  />
1071
1074
  </PageBody>
@@ -32,6 +32,7 @@ type ResponsePayload = {
32
32
  page?: number
33
33
  pageSize?: number
34
34
  totalPages: number
35
+ totalIsCapped?: boolean
35
36
  }
36
37
 
37
38
  function formatDate(value: string | null, t: (key: string) => string) {
@@ -50,6 +51,7 @@ export default function DevicesAdminListPage() {
50
51
  const [page, setPage] = React.useState(1)
51
52
  const [total, setTotal] = React.useState(0)
52
53
  const [totalPages, setTotalPages] = React.useState(1)
54
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
53
55
  const [isLoading, setIsLoading] = React.useState(true)
54
56
  const [reloadToken, setReloadToken] = React.useState(0)
55
57
  const scopeVersion = useOrganizationScopeVersion()
@@ -138,6 +140,7 @@ export default function DevicesAdminListPage() {
138
140
  setRows(Array.isArray(payload.items) ? payload.items : [])
139
141
  setTotal(payload.total || 0)
140
142
  setTotalPages(payload.totalPages || 1)
143
+ setTotalIsCapped(payload?.totalIsCapped === true)
141
144
  }
142
145
  } catch (error) {
143
146
  if (!cancelled) {
@@ -249,7 +252,7 @@ export default function DevicesAdminListPage() {
249
252
  { id: 'deactivate', label: t('devices.list.actions.deactivate'), destructive: true, onSelect: () => { void handleDeactivate(row) } },
250
253
  ]} />
251
254
  )}
252
- pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
255
+ pagination={{ page, pageSize: 50, total, totalPages, totalIsCapped, onPageChange: setPage }}
253
256
  isLoading={isLoading}
254
257
  />
255
258
  </PageBody>