, model: PricingModel) => {
+ if (event.key !== 'Enter' && event.key !== ' ') return
+ event.preventDefault()
+ handleRowClick(model)
+ },
+ [handleRowClick]
+ )
+
return (
handleRowClick(row.original)}
+ onKeyDown={(event) => handleRowKeyDown(event, row.original)}
/>
)}
/>
diff --git a/web/default/src/features/users/components/data-table-row-actions.tsx b/web/default/src/features/users/components/data-table-row-actions.tsx
index b0192501da..c92567f4bd 100644
--- a/web/default/src/features/users/components/data-table-row-actions.tsx
+++ b/web/default/src/features/users/components/data-table-row-actions.tsx
@@ -199,10 +199,8 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
)}
{
- event.preventDefault()
- setBindingDialogOpen(true)
- }}
+ closeOnClick={false}
+ onClick={() => setBindingDialogOpen(true)}
>
{t('Manage Bindings')}
@@ -211,10 +209,8 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{
- event.preventDefault()
- setSubscriptionsDialogOpen(true)
- }}
+ closeOnClick={false}
+ onClick={() => setSubscriptionsDialogOpen(true)}
>
{t('Manage Subscriptions')}
@@ -225,10 +221,8 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{
- event.preventDefault()
- setResetPasskeyOpen(true)
- }}
+ closeOnClick={false}
+ onClick={() => setResetPasskeyOpen(true)}
disabled={isRoot}
>
{t('Reset Passkey')}
@@ -238,10 +232,8 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{
- event.preventDefault()
- setResetTwoFAOpen(true)
- }}
+ closeOnClick={false}
+ onClick={() => setResetTwoFAOpen(true)}
disabled={isRoot}
>
{t('Reset 2FA')}
diff --git a/web/default/src/hooks/use-table-url-state.ts b/web/default/src/hooks/use-table-url-state.ts
index 11724d64fa..149895d3dc 100644
--- a/web/default/src/hooks/use-table-url-state.ts
+++ b/web/default/src/hooks/use-table-url-state.ts
@@ -25,11 +25,20 @@ import { useEffect, useMemo, useState } from 'react'
type SearchRecord = Record
+// Legacy global key shared with the classic theme (raw number string). Used
+// as a read fallback so preferences saved before the per-table keys existed
+// are not lost, and mirrored on write so the choice still carries over from
+// and to classic.
+const LEGACY_PAGE_SIZE_STORAGE_KEY = 'page-size'
+
function getStoredPageSize(storageKey: string | undefined): number | undefined {
if (!storageKey || typeof window === 'undefined') return undefined
try {
- const n = Number.parseInt(window.localStorage.getItem(storageKey) ?? '', 10)
+ const raw =
+ window.localStorage.getItem(storageKey) ??
+ window.localStorage.getItem(LEGACY_PAGE_SIZE_STORAGE_KEY)
+ const n = Number.parseInt(raw ?? '', 10)
return n > 0 ? n : undefined // n > 0 also rejects NaN
} catch {
return undefined
@@ -41,6 +50,7 @@ function setStoredPageSize(storageKey: string | undefined, size: number) {
try {
window.localStorage.setItem(storageKey, String(size))
+ window.localStorage.setItem(LEGACY_PAGE_SIZE_STORAGE_KEY, String(size))
} catch {
/* ignore */
}
diff --git a/web/default/src/tanstack-table.d.ts b/web/default/src/tanstack-table.d.ts
index 3fdc78ab45..1c47d9d097 100644
--- a/web/default/src/tanstack-table.d.ts
+++ b/web/default/src/tanstack-table.d.ts
@@ -32,8 +32,9 @@ declare module '@tanstack/react-table' {
*/
contentMode?: 'full' | 'wrap' | 'summary'
/**
- * Responsive card placement. Secondary fields stay accessible behind the
- * shared details disclosure; only duplicate/technical fields use hidden.
+ * Responsive card placement. Primary and secondary fields are both always
+ * visible (secondary marks lower-emphasis details); only duplicate or
+ * technical fields use hidden.
*/
cardRole?: 'title' | 'badge' | 'primary' | 'secondary' | 'hidden'
cardOrder?: number