Skip to content

Commit c21b88c

Browse files
committed
Update apikeys page to use mui
1 parent 2a0dcc1 commit c21b88c

File tree

3 files changed

+100
-83
lines changed

3 files changed

+100
-83
lines changed

src/apps/dashboard/features/keys/components/ApiKeyCell.tsx

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/apps/dashboard/routes/keys/index.tsx

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,95 @@
11
import Page from 'components/Page';
2-
import SectionTitleContainer from 'elements/SectionTitleContainer';
32
import { useApi } from 'hooks/useApi';
43
import globalize from 'lib/globalize';
5-
import React, { useCallback } from 'react';
4+
import React, { useCallback, useMemo } from 'react';
65
import type { AuthenticationInfo } from '@jellyfin/sdk/lib/generated-client/models/authentication-info';
7-
import Loading from 'components/loading/LoadingComponent';
86
import confirm from 'components/confirm/confirm';
9-
import ApiKeyCell from 'apps/dashboard/features/keys/components/ApiKeyCell';
107
import { useApiKeys } from 'apps/dashboard/features/keys/api/useApiKeys';
118
import { useRevokeKey } from 'apps/dashboard/features/keys/api/useRevokeKey';
129
import { useCreateKey } from 'apps/dashboard/features/keys/api/useCreateKey';
10+
import { Box, Button, IconButton, Stack, Tooltip, Typography } from '@mui/material';
11+
import { MaterialReactTable, MRT_ColumnDef, useMaterialReactTable } from 'material-react-table';
12+
import { getDisplayTime, parseISO8601Date, toLocaleDateString } from 'scripts/datetime';
13+
import DeleteIcon from '@mui/icons-material/Delete';
1314

1415
const ApiKeys = () => {
1516
const { api } = useApi();
1617
const { data: keys, isLoading } = useApiKeys();
1718
const revokeKey = useRevokeKey();
1819
const createKey = useCreateKey();
1920

21+
const columns = useMemo<MRT_ColumnDef<AuthenticationInfo>[]>(() => [
22+
{
23+
id: 'ApiKey',
24+
accessorKey: 'AccessToken',
25+
header: globalize.translate('HeaderApiKey'),
26+
size: 300
27+
},
28+
{
29+
id: 'AppName',
30+
accessorKey: 'AppName',
31+
header: globalize.translate('HeaderApp')
32+
},
33+
{
34+
id: 'DateIssued',
35+
accessorFn: item => parseISO8601Date(item.DateCreated),
36+
Cell: ({ cell }) => toLocaleDateString(cell.getValue<Date>()) + ' ' + getDisplayTime(cell.getValue<Date>()),
37+
header: globalize.translate('HeaderDateIssued'),
38+
filterVariant: 'datetime-range'
39+
}
40+
], []);
41+
42+
const table = useMaterialReactTable({
43+
columns,
44+
data: keys?.Items || [],
45+
46+
state: {
47+
isLoading
48+
},
49+
50+
rowCount: keys?.TotalRecordCount || 0,
51+
52+
enableColumnPinning: true,
53+
enableColumnResizing: true,
54+
55+
enableStickyFooter: true,
56+
enableStickyHeader: true,
57+
muiTableContainerProps: {
58+
sx: {
59+
maxHeight: 'calc(100% - 7rem)' // 2 x 3.5rem for header and footer
60+
}
61+
},
62+
63+
// Enable (delete) row actions
64+
enableRowActions: true,
65+
displayColumnDefOptions: {
66+
'mrt-row-actions': {
67+
header: '',
68+
size: 25
69+
}
70+
},
71+
72+
renderTopToolbarCustomActions: () => (
73+
<Button onClick={showNewKeyPopup}>{globalize.translate('ApiKeysNew')}</Button>
74+
),
75+
76+
renderRowActions: ({ row }) => {
77+
return (
78+
<Box sx={{ display: 'flex' }}>
79+
<Tooltip title={globalize.translate('ButtonRevoke')}>
80+
<IconButton
81+
color='error'
82+
// eslint-disable-next-line react/jsx-no-bind
83+
onClick={() => row.original?.AccessToken && onRevokeKey(row.original.AccessToken)}
84+
>
85+
<DeleteIcon />
86+
</IconButton>
87+
</Tooltip>
88+
</Box>
89+
);
90+
}
91+
});
92+
2093
const onRevokeKey = useCallback((accessToken: string) => {
2194
if (!api) return;
2295

@@ -49,45 +122,34 @@ const ApiKeys = () => {
49122
});
50123
}, [api, createKey]);
51124

52-
if (isLoading) {
53-
return <Loading />;
54-
}
55-
56125
return (
57126
<Page
58127
id='apiKeysPage'
59128
title={globalize.translate('HeaderApiKeys')}
60129
className='mainAnimatedPage type-interior'
61130
>
62-
<div className='content-primary'>
63-
<SectionTitleContainer
64-
title={globalize.translate('HeaderApiKeys')}
65-
isBtnVisible={true}
66-
btnId='btnAddSchedule'
67-
btnClassName='fab submit sectionTitleButton btnNewKey'
68-
btnTitle={globalize.translate('Add')}
69-
btnIcon='add'
70-
onClick={showNewKeyPopup}
71-
/>
72-
<p>{globalize.translate('HeaderApiKeysHelp')}</p>
73-
<br />
74-
<table className='tblApiKeys detailTable'>
75-
<caption className='clipForScreenReader'>{globalize.translate('ApiKeysCaption')}</caption>
76-
<thead>
77-
<tr>
78-
<th scope='col' className='detailTableHeaderCell'></th>
79-
<th scope='col' className='detailTableHeaderCell'>{globalize.translate('HeaderApiKey')}</th>
80-
<th scope='col' className='detailTableHeaderCell'>{globalize.translate('HeaderApp')}</th>
81-
<th scope='col' className='detailTableHeaderCell'>{globalize.translate('HeaderDateIssued')}</th>
82-
</tr>
83-
</thead>
84-
<tbody className='resultBody'>
85-
{keys?.Items?.map((key: AuthenticationInfo) => {
86-
return <ApiKeyCell key={key.AccessToken} apiKey={key} revokeKey={onRevokeKey} />;
87-
})}
88-
</tbody>
89-
</table>
90-
</div>
131+
<Box
132+
className='content-primary'
133+
sx={{
134+
display: 'flex',
135+
flexDirection: 'column',
136+
height: '100%'
137+
}}
138+
>
139+
<Box
140+
sx={{
141+
marginBottom: 1
142+
}}
143+
>
144+
<Stack spacing={2}>
145+
<Typography variant='h2'>
146+
{globalize.translate('HeaderApiKeys')}
147+
</Typography>
148+
<Typography>{globalize.translate('HeaderApiKeysHelp')}</Typography>
149+
</Stack>
150+
</Box>
151+
<MaterialReactTable table={table} />
152+
</Box>
91153
</Page>
92154
);
93155
};

src/strings/en-us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"AnyLanguage": "Any Language",
5959
"Anytime": "Anytime",
6060
"ApiKeysCaption": "List of the currently enabled API keys",
61+
"ApiKeysNew": "New API Key",
6162
"AroundTime": "Around {0}",
6263
"Arranger": "Arranger",
6364
"Art": "Clearart",

0 commit comments

Comments
 (0)