1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Refactor cell components

This commit is contained in:
Bill Thornton 2024-08-27 10:49:15 -04:00
parent c7ed7ed48f
commit cb906678e6
8 changed files with 103 additions and 52 deletions

View file

@ -4,7 +4,7 @@ import type { Api } from '@jellyfin/sdk';
import { getActivityLogApi } from '@jellyfin/sdk/lib/utils/api/activity-log-api';
import { useQuery } from '@tanstack/react-query';
import { useApi } from './useApi';
import { useApi } from 'hooks/useApi';
const fetchLogEntries = async (
api?: Api,

View file

@ -0,0 +1,22 @@
import IconButton from '@mui/material/IconButton/IconButton';
import PermMedia from '@mui/icons-material/PermMedia';
import React, { type FC } from 'react';
import { Link } from 'react-router-dom';
import type { ActivityLogEntryCell } from 'apps/dashboard/features/activity/types/ActivityLogEntryCell';
import globalize from 'lib/globalize';
const ActionsCell: FC<ActivityLogEntryCell> = ({ row }) => (
row.original.ItemId ? (
<IconButton
size='large'
title={globalize.translate('LabelMediaDetails')}
component={Link}
to={`/details?id=${row.original.ItemId}`}
>
<PermMedia fontSize='inherit' />
</IconButton>
) : undefined
);
export default ActionsCell;

View file

@ -0,0 +1,13 @@
import type { LogLevel } from '@jellyfin/sdk/lib/generated-client/models/log-level';
import React, { type FC } from 'react';
import { ActivityLogEntryCell } from '../types/ActivityLogEntryCell';
import LogLevelChip from './LogLevelChip';
const LogLevelCell: FC<ActivityLogEntryCell> = ({ cell }) => (
cell.getValue<LogLevel | undefined>() ? (
<LogLevelChip level={cell.getValue<LogLevel>()} />
) : undefined
);
export default LogLevelCell;

View file

@ -1,12 +1,14 @@
import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry';
import Info from '@mui/icons-material/Info';
import Box from '@mui/material/Box';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import React, { FC, useCallback, useState } from 'react';
import React, { type FC, useCallback, useState } from 'react';
const OverviewCell: FC<ActivityLogEntry> = ({ Overview, ShortOverview }) => {
import type { ActivityLogEntryCell } from '../types/ActivityLogEntryCell';
const OverviewCell: FC<ActivityLogEntryCell> = ({ row }) => {
const { ShortOverview, Overview } = row.original;
const displayValue = ShortOverview ?? Overview;
const [ open, setOpen ] = useState(false);

View file

@ -0,0 +1,27 @@
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
import IconButton from '@mui/material/IconButton/IconButton';
import React, { type FC } from 'react';
import { Link } from 'react-router-dom';
import UserAvatar from 'components/UserAvatar';
interface UserAvatarButtonProps {
user?: UserDto
}
const UserAvatarButton: FC<UserAvatarButtonProps> = ({ user }) => (
user?.Id ? (
<IconButton
size='large'
color='inherit'
sx={{ padding: 0 }}
title={user.Name || undefined}
component={Link}
to={`/dashboard/users/profile?userId=${user.Id}`}
>
<UserAvatar user={user} />
</IconButton>
) : undefined
);
export default UserAvatarButton;

View file

@ -0,0 +1,7 @@
import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry';
import type { MRT_Cell, MRT_Row } from 'material-react-table';
export interface ActivityLogEntryCell {
cell: MRT_Cell<ActivityLogEntry>
row: MRT_Row<ActivityLogEntry>
}

View file

@ -1,34 +1,34 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { ActivityLogEntry } from '@jellyfin/sdk/lib/generated-client/models/activity-log-entry';
import { LogLevel } from '@jellyfin/sdk/lib/generated-client/models/log-level';
import type { UserDto } from '@jellyfin/sdk/lib/generated-client/models/user-dto';
import PermMedia from '@mui/icons-material/PermMedia';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import Typography from '@mui/material/Typography';
import { type MRT_Cell, type MRT_ColumnDef, type MRT_Row, MaterialReactTable, useMaterialReactTable } from 'material-react-table';
import { Link, useSearchParams } from 'react-router-dom';
import { type MRT_ColumnDef, MaterialReactTable, useMaterialReactTable } from 'material-react-table';
import { useSearchParams } from 'react-router-dom';
import { useLogEntires } from 'apps/dashboard/features/activity/api/useLogEntries';
import ActionsCell from 'apps/dashboard/features/activity/components/ActionsCell';
import LogLevelCell from 'apps/dashboard/features/activity/components/LogLevelCell';
import OverviewCell from 'apps/dashboard/features/activity/components/OverviewCell';
import UserAvatarButton from 'apps/dashboard/features/activity/components/UserAvatarButton';
import type { ActivityLogEntryCell } from 'apps/dashboard/features/activity/types/ActivityLogEntryCell';
import Page from 'components/Page';
import UserAvatar from 'components/UserAvatar';
import { useLogEntires } from 'hooks/useLogEntries';
import { useUsers } from 'hooks/useUsers';
import { parseISO8601Date, toLocaleString } from 'scripts/datetime';
import globalize from 'lib/globalize';
import { toBoolean } from 'utils/string';
import LogLevelChip from '../components/activityTable/LogLevelChip';
import OverviewCell from '../components/activityTable/OverviewCell';
type UsersRecords = Record<string, UserDto>;
const DEFAULT_PAGE_SIZE = 25;
const VIEW_PARAM = 'useractivity';
const enum ActivityView {
All,
User,
System
All = 'All',
User = 'User',
System = 'System'
}
const getActivityView = (param: string | null) => {
@ -37,6 +37,12 @@ const getActivityView = (param: string | null) => {
return ActivityView.System;
};
const getUserCell = (users: UsersRecords) => function UserCell({ row }: ActivityLogEntryCell) {
return (
<UserAvatarButton user={row.original.UserId && users[row.original.UserId] || undefined} />
);
};
const Activity = () => {
const [ searchParams, setSearchParams ] = useSearchParams();
@ -50,7 +56,6 @@ const Activity = () => {
const { data: usersData, isLoading: isUsersLoading } = useUsers();
type UsersRecords = Record<string, UserDto>;
const users: UsersRecords = useMemo(() => {
if (!usersData) return {};
@ -65,6 +70,8 @@ const Activity = () => {
}, {});
}, [usersData]);
const UserCell = getUserCell(users);
const activityParams = useMemo(() => ({
startIndex: pagination.pageIndex * pagination.pageSize,
limit: pagination.pageSize,
@ -87,11 +94,7 @@ const Activity = () => {
accessorKey: 'Severity',
header: globalize.translate('LabelLevel'),
size: 90,
Cell: ({ cell }: { cell: MRT_Cell<ActivityLogEntry> }) => (
cell.getValue<LogLevel | undefined>() ? (
<LogLevelChip level={cell.getValue<LogLevel>()} />
) : undefined
),
Cell: LogLevelCell,
enableResizing: false,
muiTableBodyCellProps: {
align: 'center'
@ -102,20 +105,7 @@ const Activity = () => {
accessorFn: row => row.UserId && users[row.UserId]?.Name,
header: globalize.translate('LabelUser'),
size: 75,
Cell: ({ row }: { row: MRT_Row<ActivityLogEntry> }) => (
row.original.UserId ? (
<IconButton
size='large'
color='inherit'
sx={{ padding: 0 }}
title={users[row.original.UserId]?.Name || undefined}
component={Link}
to={`/dashboard/users/profile?userId=${row.original.UserId}`}
>
<UserAvatar user={users[row.original.UserId]} />
</IconButton>
) : undefined
),
Cell: UserCell,
enableResizing: false,
visibleInShowHideMenu: activityView !== ActivityView.System,
muiTableBodyCellProps: {
@ -132,9 +122,7 @@ const Activity = () => {
accessorFn: row => row.ShortOverview || row.Overview,
header: globalize.translate('LabelOverview'),
size: 170,
Cell: ({ row }: { row: MRT_Row<ActivityLogEntry> }) => (
<OverviewCell {...row.original} />
)
Cell: OverviewCell
},
{
accessorKey: 'Type',
@ -146,24 +134,13 @@ const Activity = () => {
accessorFn: row => row.ItemId,
header: '',
size: 60,
Cell: ({ row }: { row: MRT_Row<ActivityLogEntry> }) => (
row.original.ItemId ? (
<IconButton
size='large'
title={globalize.translate('LabelMediaDetails')}
component={Link}
to={`/details?id=${row.original.ItemId}`}
>
<PermMedia fontSize='inherit' />
</IconButton>
) : undefined
),
Cell: ActionsCell,
enableColumnActions: false,
enableColumnFilter: false,
enableResizing: false,
enableSorting: false
}
], [ activityView, users ]);
], [ UserCell, activityView, users ]);
const onViewChange = useCallback((_e: React.MouseEvent<HTMLElement, MouseEvent>, newView: ActivityView | null) => {
if (newView !== null) {
@ -201,6 +178,9 @@ const Activity = () => {
},
// State
initialState: {
density: 'compact'
},
state: {
isLoading,
pagination,