Migrate paths dashboard widget to react and add storage metrics

This commit is contained in:
Bill Thornton
2025-06-03 16:28:22 -04:00
parent a938650ded
commit 09063d3376
11 changed files with 322 additions and 81 deletions
@@ -0,0 +1,28 @@
import type { Api } from '@jellyfin/sdk';
import { getSystemApi } from '@jellyfin/sdk/lib/utils/api/system-api';
import { queryOptions, useQuery } from '@tanstack/react-query';
import type { AxiosRequestConfig } from 'axios';
import { useApi } from 'hooks/useApi';
const fetchSystemStorage = async (
api: Api,
options?: AxiosRequestConfig
) => {
const response = await getSystemApi(api)
.getSystemStorage(options);
return response.data;
};
const getSystemStorageQuery = (
api?: Api
) => queryOptions({
queryKey: [ 'SystemStorage' ],
queryFn: ({ signal }) => fetchSystemStorage(api!, { signal }),
enabled: !!api
});
export const useSystemStorage = () => {
const { api } = useApi();
return useQuery(getSystemStorageQuery(api));
};
@@ -0,0 +1,105 @@
import type { FolderStorageDto } from '@jellyfin/sdk/lib/generated-client';
import LinearProgress from '@mui/material/LinearProgress';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Skeleton from '@mui/material/Skeleton';
import Typography from '@mui/material/Typography';
import React, { type FC } from 'react';
import globalize from 'lib/globalize';
import { getReadableSize } from 'utils/file';
import { StorageType } from '../constants/StorageType';
import StorageTypeIcon from './StorageTypeIcon';
interface StorageListItemProps {
label: string
folder?: FolderStorageDto
}
const calculateUsed = (folder?: FolderStorageDto) => {
if (typeof folder?.UsedSpace === 'undefined') return 0;
if (typeof folder.FreeSpace === 'undefined') return 100;
return folder.UsedSpace / (folder.FreeSpace + folder.UsedSpace) * 100;
};
const getStatusColor = (percent: number) => {
if (percent >= 90) return 'error';
if (percent >= 80) return 'warning';
return 'success';
};
const getStorageTypeText = (type?: string | null) => {
if (!type) return undefined;
if (Object.keys(StorageType).includes(type)) {
return globalize.translate(`StorageType.${type}`);
}
return type;
};
const StorageListItem: FC<StorageListItemProps> = ({
label,
folder
}) => {
const usedSpace = (typeof folder?.UsedSpace === 'undefined') ? '?' : getReadableSize(folder.UsedSpace);
const totalSpace = (typeof folder?.FreeSpace === 'undefined' || typeof folder.UsedSpace === 'undefined') ?
'?' : getReadableSize(folder.FreeSpace + folder.UsedSpace);
const usedPercent = calculateUsed(folder);
const statusColor = folder ? getStatusColor(usedPercent) : 'primary';
return (
<ListItem>
<ListItemIcon title={getStorageTypeText(folder?.StorageType)}>
<StorageTypeIcon type={folder?.StorageType} />
</ListItemIcon>
<ListItemText
primary={
<Typography
component='span'
variant='body2'
>
{label}
</Typography>
}
secondary={
<>
<Typography
color='textPrimary'
sx={{
paddingBottom: 0.5
}}
>
{folder ? folder.Path : (
<Skeleton />
)}
</Typography>
<LinearProgress
variant={folder ? 'determinate' : 'indeterminate'}
color={statusColor}
value={usedPercent}
/>
<Typography
variant='body2'
color='textSecondary'
sx={{
textAlign: 'end'
}}
>
{`${usedSpace} / ${totalSpace}`}
</Typography>
</>
}
slots={{
secondary: 'div'
}}
/>
</ListItem>
);
};
export default StorageListItem;
@@ -0,0 +1,31 @@
import Album from '@mui/icons-material/Album';
import Lan from '@mui/icons-material/Lan';
import Memory from '@mui/icons-material/Memory';
import Storage from '@mui/icons-material/Storage';
import Usb from '@mui/icons-material/Usb';
import React, { type FC } from 'react';
import { StorageType } from '../constants/StorageType';
interface StorageTypeIconProps {
type?: string | null
}
const StorageTypeIcon: FC<StorageTypeIconProps> = ({
type
}) => {
switch (type) {
case StorageType.CDRom:
return <Album />;
case StorageType.Network:
return <Lan />;
case StorageType.Ram:
return <Memory />;
case StorageType.Removable:
return <Usb />;
default:
return <Storage />;
}
};
export default StorageTypeIcon;
@@ -0,0 +1,13 @@
/**
* Common storage type values returned by the API.
* NOTE: This list is not comprehensive as .NET might return any string.
*/
export enum StorageType {
CDRom = 'CDRom',
Fixed = 'Fixed',
Network = 'Network',
NoRootDirectory = 'NoRootDirectory',
Ram = 'Ram',
Removable = 'Removable',
Unknown = 'Unknown'
};