From 817b59bd6f268d11cb604a6bad4f98f9887ee0bb Mon Sep 17 00:00:00 2001 From: viown <48097677+viown@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:18:23 +0300 Subject: [PATCH] Update apikeys page to use mui --- .../features/keys/components/ApiKeyCell.tsx | 46 ------ src/apps/dashboard/routes/keys/index.tsx | 136 +++++++++++++----- 2 files changed, 99 insertions(+), 83 deletions(-) delete mode 100644 src/apps/dashboard/features/keys/components/ApiKeyCell.tsx diff --git a/src/apps/dashboard/features/keys/components/ApiKeyCell.tsx b/src/apps/dashboard/features/keys/components/ApiKeyCell.tsx deleted file mode 100644 index 6604b0dd1f1..00000000000 --- a/src/apps/dashboard/features/keys/components/ApiKeyCell.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { FunctionComponent, useCallback } from 'react'; -import type { AuthenticationInfo } from '@jellyfin/sdk/lib/generated-client/models/authentication-info'; -import ButtonElement from 'elements/ButtonElement'; -import datetime from 'scripts/datetime'; -import globalize from 'lib/globalize'; - -type ApiKeyCellProps = { - apiKey: AuthenticationInfo; - revokeKey?: (accessToken: string) => void; -}; - -const ApiKeyCell: FunctionComponent = ({ apiKey, revokeKey }: ApiKeyCellProps) => { - const getDate = (dateCreated: string | undefined) => { - const date = datetime.parseISO8601Date(dateCreated, true); - return datetime.toLocaleDateString(date) + ' ' + datetime.getDisplayTime(date); - }; - - const onClick = useCallback(() => { - if (apiKey?.AccessToken && revokeKey !== undefined) { - revokeKey(apiKey.AccessToken); - } - }, [apiKey, revokeKey]); - - return ( - - - - - - {apiKey.AccessToken} - - - {apiKey.AppName} - - - {getDate(apiKey.DateCreated)} - - - ); -}; - -export default ApiKeyCell; diff --git a/src/apps/dashboard/routes/keys/index.tsx b/src/apps/dashboard/routes/keys/index.tsx index 3c19b42062f..f1886712be8 100644 --- a/src/apps/dashboard/routes/keys/index.tsx +++ b/src/apps/dashboard/routes/keys/index.tsx @@ -1,15 +1,16 @@ import Page from 'components/Page'; -import SectionTitleContainer from 'elements/SectionTitleContainer'; import { useApi } from 'hooks/useApi'; import globalize from 'lib/globalize'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import type { AuthenticationInfo } from '@jellyfin/sdk/lib/generated-client/models/authentication-info'; -import Loading from 'components/loading/LoadingComponent'; import confirm from 'components/confirm/confirm'; -import ApiKeyCell from 'apps/dashboard/features/keys/components/ApiKeyCell'; 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, Button, IconButton, Stack, Tooltip, Typography } from '@mui/material'; +import { MaterialReactTable, MRT_ColumnDef, useMaterialReactTable } from 'material-react-table'; +import { getDisplayTime, parseISO8601Date, toLocaleDateString } from 'scripts/datetime'; +import DeleteIcon from '@mui/icons-material/Delete'; const ApiKeys = () => { const { api } = useApi(); @@ -17,6 +18,78 @@ const ApiKeys = () => { const revokeKey = useRevokeKey(); const createKey = useCreateKey(); + const columns = useMemo[]>(() => [ + { + id: 'ApiKey', + accessorKey: 'AccessToken', + header: globalize.translate('HeaderApiKey'), + size: 300 + }, + { + id: 'AppName', + accessorKey: 'AppName', + header: globalize.translate('HeaderApp') + }, + { + id: 'DateIssued', + accessorFn: item => parseISO8601Date(item.DateCreated), + Cell: ({ cell }) => toLocaleDateString(cell.getValue()) + ' ' + getDisplayTime(cell.getValue()), + header: globalize.translate('HeaderDateIssued'), + filterVariant: 'datetime-range' + } + ], []); + + const table = useMaterialReactTable({ + columns, + data: keys?.Items || [], + + 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, + displayColumnDefOptions: { + 'mrt-row-actions': { + header: '', + size: 25 + } + }, + + renderTopToolbarCustomActions: () => ( + + ), + + renderRowActions: ({ row }) => { + return ( + + + row.original?.AccessToken && onRevokeKey(row.original.AccessToken)} + > + + + + + ); + } + }); + const onRevokeKey = useCallback((accessToken: string) => { if (!api) return; @@ -49,45 +122,34 @@ const ApiKeys = () => { }); }, [api, createKey]); - if (isLoading) { - return ; - } - return ( -
- -

{globalize.translate('HeaderApiKeysHelp')}

-
- - - - - - - - - - - - {keys?.Items?.map((key: AuthenticationInfo) => { - return ; - })} - -
{globalize.translate('ApiKeysCaption')}
{globalize.translate('HeaderApiKey')}{globalize.translate('HeaderApp')}{globalize.translate('HeaderDateIssued')}
-
+ + + + + {globalize.translate('HeaderApiKeys')} + + {globalize.translate('HeaderApiKeysHelp')} + + + +
); };