Migrate paths dashboard widget to react and add storage metrics
This commit is contained in:
@@ -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'
|
||||
};
|
||||
Reference in New Issue
Block a user