From d7595a74541a92945a7cda6995ee6fb45055b835 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Fri, 14 Feb 2025 15:28:27 -0500 Subject: [PATCH 1/2] Refactor api keys to use TablePage component --- .../dashboard/components/table/TablePage.tsx | 13 ++- src/apps/dashboard/routes/activity/index.tsx | 4 +- src/apps/dashboard/routes/keys/index.tsx | 84 +++++++------------ 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/src/apps/dashboard/components/table/TablePage.tsx b/src/apps/dashboard/components/table/TablePage.tsx index 4e5daef2a5..d9724c9d1b 100644 --- a/src/apps/dashboard/components/table/TablePage.tsx +++ b/src/apps/dashboard/components/table/TablePage.tsx @@ -1,4 +1,5 @@ import Box from '@mui/material/Box/Box'; +import Stack from '@mui/material/Stack/Stack'; import Typography from '@mui/material/Typography/Typography'; import { type MRT_RowData, type MRT_TableInstance, MaterialReactTable } from 'material-react-table'; import React from 'react'; @@ -7,6 +8,7 @@ import Page, { type PageProps } from 'components/Page'; interface TablePageProps extends PageProps { title: string + subtitle?: string table: MRT_TableInstance } @@ -27,6 +29,7 @@ export const DEFAULT_TABLE_OPTIONS = { const TablePage = ({ title, + subtitle, table, children, ...pageProps @@ -44,7 +47,8 @@ const TablePage = ({ height: '100%' }} > - ({ {title} - + {subtitle && ( + + {subtitle} + + )} + {children} diff --git a/src/apps/dashboard/routes/activity/index.tsx b/src/apps/dashboard/routes/activity/index.tsx index 3e46c9e5e8..6736e17bb4 100644 --- a/src/apps/dashboard/routes/activity/index.tsx +++ b/src/apps/dashboard/routes/activity/index.tsx @@ -40,7 +40,7 @@ const getUserCell = (users: UsersRecords) => function UserCell({ row }: Activity ); }; -const Activity = () => { +export const Component = () => { const [ searchParams, setSearchParams ] = useSearchParams(); const [ activityView, setActivityView ] = useState( @@ -201,4 +201,4 @@ const Activity = () => { ); }; -export default Activity; +Component.displayName = 'ActivityPage'; diff --git a/src/apps/dashboard/routes/keys/index.tsx b/src/apps/dashboard/routes/keys/index.tsx index 54c59d0da9..2d5ea44270 100644 --- a/src/apps/dashboard/routes/keys/index.tsx +++ b/src/apps/dashboard/routes/keys/index.tsx @@ -1,26 +1,25 @@ -import parseISO from 'date-fns/parseISO'; - -import DateTimeCell from 'apps/dashboard/components/table/DateTimeCell'; -import Page from 'components/Page'; -import { useApi } from 'hooks/useApi'; -import globalize from 'lib/globalize'; -import React, { useCallback, useMemo } from 'react'; import type { AuthenticationInfo } from '@jellyfin/sdk/lib/generated-client/models/authentication-info'; -import confirm from 'components/confirm/confirm'; -import { useApiKeys } from 'apps/dashboard/features/keys/api/useApiKeys'; -import { useRevokeKey } from 'apps/dashboard/features/keys/api/useRevokeKey'; -import { useCreateKey } from 'apps/dashboard/features/keys/api/useCreateKey'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; -import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; -import { MaterialReactTable, MRT_ColumnDef, useMaterialReactTable } from 'material-react-table'; -import DeleteIcon from '@mui/icons-material/Delete'; import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import parseISO from 'date-fns/parseISO'; +import { type MRT_ColumnDef, useMaterialReactTable } from 'material-react-table'; +import React, { useCallback, useMemo } from 'react'; -const ApiKeys = () => { +import DateTimeCell from 'apps/dashboard/components/table/DateTimeCell'; +import TablePage from 'apps/dashboard/components/table/TablePage'; +import { useApiKeys } from 'apps/dashboard/features/keys/api/useApiKeys'; +import { useRevokeKey } from 'apps/dashboard/features/keys/api/useRevokeKey'; +import { useCreateKey } from 'apps/dashboard/features/keys/api/useCreateKey'; +import confirm from 'components/confirm/confirm'; +import prompt from 'components/prompt/prompt'; +import { useApi } from 'hooks/useApi'; +import globalize from 'lib/globalize'; + +export const Component = () => { const { api } = useApi(); const { data: keys, isLoading } = useApiKeys(); const revokeKey = useRevokeKey(); @@ -119,53 +118,28 @@ const ApiKeys = () => { const showNewKeyPopup = useCallback(() => { if (!api) return; - import('../../../../components/prompt/prompt').then(({ default: prompt }) => { - prompt({ - title: globalize.translate('HeaderNewApiKey'), - label: globalize.translate('LabelAppName'), - description: globalize.translate('LabelAppNameExample') - }).then((value) => { - createKey.mutate({ - app: value - }); - }).catch(() => { - // popup closed + prompt({ + title: globalize.translate('HeaderNewApiKey'), + label: globalize.translate('LabelAppName'), + description: globalize.translate('LabelAppNameExample') + }).then((value) => { + createKey.mutate({ + app: value }); - }).catch(err => { - console.error('[apikeys] failed to load api key popup', err); + }).catch(() => { + // popup closed }); }, [api, createKey]); return ( - - - - - - {globalize.translate('HeaderApiKeys')} - - {globalize.translate('HeaderApiKeysHelp')} - - - - - + table={table} + /> ); }; -export default ApiKeys; +Component.displayName = 'ApiKeysPage'; From ce8bc964f63087bd5b0f564a2662e0b242e9def7 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Sun, 16 Feb 2025 03:35:00 -0500 Subject: [PATCH 2/2] Memoize table data --- src/apps/dashboard/routes/activity/index.tsx | 12 +++++++--- src/apps/dashboard/routes/keys/index.tsx | 24 +++++++------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/apps/dashboard/routes/activity/index.tsx b/src/apps/dashboard/routes/activity/index.tsx index 6736e17bb4..09547af0b7 100644 --- a/src/apps/dashboard/routes/activity/index.tsx +++ b/src/apps/dashboard/routes/activity/index.tsx @@ -61,7 +61,13 @@ export const Component = () => { hasUserId: activityView !== ActivityView.All ? activityView === ActivityView.User : undefined }), [activityView, pagination.pageIndex, pagination.pageSize]); - const { data: logEntries, isLoading: isLogEntriesLoading } = useLogEntries(activityParams); + const { data, isLoading: isLogEntriesLoading } = useLogEntries(activityParams); + const logEntries = useMemo(() => ( + data?.Items || [] + ), [ data ]); + const rowCount = useMemo(() => ( + data?.TotalRecordCount || 0 + ), [ data ]); const isLoading = isUsersLoading || isLogEntriesLoading; @@ -154,7 +160,7 @@ export const Component = () => { ...DEFAULT_TABLE_OPTIONS, columns, - data: logEntries?.Items || [], + data: logEntries, // State initialState: { @@ -168,7 +174,7 @@ export const Component = () => { // Server pagination manualPagination: true, onPaginationChange: setPagination, - rowCount: logEntries?.TotalRecordCount || 0, + rowCount, // Custom toolbar contents renderTopToolbarCustomActions: () => ( diff --git a/src/apps/dashboard/routes/keys/index.tsx b/src/apps/dashboard/routes/keys/index.tsx index 2d5ea44270..1abb66884f 100644 --- a/src/apps/dashboard/routes/keys/index.tsx +++ b/src/apps/dashboard/routes/keys/index.tsx @@ -10,7 +10,7 @@ import { type MRT_ColumnDef, useMaterialReactTable } from 'material-react-table' import React, { useCallback, useMemo } from 'react'; import DateTimeCell from 'apps/dashboard/components/table/DateTimeCell'; -import TablePage from 'apps/dashboard/components/table/TablePage'; +import TablePage, { DEFAULT_TABLE_OPTIONS } from 'apps/dashboard/components/table/TablePage'; import { useApiKeys } from 'apps/dashboard/features/keys/api/useApiKeys'; import { useRevokeKey } from 'apps/dashboard/features/keys/api/useRevokeKey'; import { useCreateKey } from 'apps/dashboard/features/keys/api/useCreateKey'; @@ -21,7 +21,10 @@ import globalize from 'lib/globalize'; export const Component = () => { const { api } = useApi(); - const { data: keys, isLoading } = useApiKeys(); + const { data, isLoading } = useApiKeys(); + const keys = useMemo(() => ( + data?.Items || [] + ), [ data ]); const revokeKey = useRevokeKey(); const createKey = useCreateKey(); @@ -47,26 +50,15 @@ export const Component = () => { ], []); const table = useMaterialReactTable({ + ...DEFAULT_TABLE_OPTIONS, + columns, - data: keys?.Items || [], + data: keys, state: { isLoading }, - rowCount: keys?.TotalRecordCount || 0, - - enableColumnPinning: true, - enableColumnResizing: true, - - enableStickyFooter: true, - enableStickyHeader: true, - muiTableContainerProps: { - sx: { - maxHeight: 'calc(100% - 7rem)' // 2 x 3.5rem for header and footer - } - }, - // Enable (delete) row actions enableRowActions: true, positionActionsColumn: 'last',