mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
feat(plugins UI): enhance PluginShow with author, website, and permissions display
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
0cd44d2960
commit
78f5ffce99
@ -338,6 +338,9 @@
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"version": "Version",
|
||||
"author": "Author",
|
||||
"website": "Website",
|
||||
"permissions": "Permissions",
|
||||
"enabled": "Enabled",
|
||||
"status": "Status",
|
||||
"path": "Path",
|
||||
|
||||
@ -22,9 +22,12 @@ import {
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Chip,
|
||||
Tooltip,
|
||||
Link,
|
||||
} from '@material-ui/core'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { MdExpandMore, MdError, MdCheckCircle } from 'react-icons/md'
|
||||
import { MdExpandMore, MdError } from 'react-icons/md'
|
||||
import { Title, DateField } from '../common'
|
||||
import { validateJson } from './jsonValidation'
|
||||
|
||||
@ -71,15 +74,6 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.85rem',
|
||||
},
|
||||
statusEnabled: {
|
||||
color: theme.palette.success?.main || theme.palette.primary.main,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(0.5),
|
||||
},
|
||||
statusDisabled: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
toolbar: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
@ -104,8 +98,63 @@ const useStyles = makeStyles((theme) => ({
|
||||
fontSize: '0.85rem',
|
||||
wordBreak: 'break-all',
|
||||
},
|
||||
permissionsContainer: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: theme.spacing(0.5),
|
||||
},
|
||||
permissionChip: {
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
tooltipContent: {
|
||||
'& code': {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.8em',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
padding: '1px 4px',
|
||||
borderRadius: 2,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
const PermissionChip = ({ label, permission }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
if (!permission) return null
|
||||
|
||||
const hasHosts = permission.allowedHosts?.length > 0
|
||||
const tooltipContent = (
|
||||
<Box className={classes.tooltipContent}>
|
||||
{permission.reason && <Typography variant="body2">{permission.reason}</Typography>}
|
||||
{hasHosts && (
|
||||
<Box mt={permission.reason ? 0.5 : 0}>
|
||||
<Typography variant="caption" component="div">
|
||||
Allowed hosts: {permission.allowedHosts.map((host, i) => (
|
||||
<span key={host}>{i > 0 && ', '}<code>{host}</code></span>
|
||||
))}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
|
||||
const hasTooltip = permission.reason || hasHosts
|
||||
|
||||
const chip = (
|
||||
<Chip
|
||||
size="small"
|
||||
label={label}
|
||||
className={classes.permissionChip}
|
||||
/>
|
||||
)
|
||||
|
||||
return hasTooltip ? (
|
||||
<Tooltip title={tooltipContent} arrow>
|
||||
{chip}
|
||||
</Tooltip>
|
||||
) : chip
|
||||
}
|
||||
|
||||
const PluginTitle = ({ record }) => {
|
||||
const translate = useTranslate()
|
||||
const resourceName = translate('resources.plugin.name', { smart_count: 1 })
|
||||
@ -202,29 +251,12 @@ const PluginShowContent = () => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Status and Enable/Disable */}
|
||||
{/* Status - Enable/Disable Switch Only */}
|
||||
<Card className={classes.section}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" className={classes.sectionTitle}>
|
||||
{translate('resources.plugin.sections.status')}
|
||||
</Typography>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box>
|
||||
{record.enabled ? (
|
||||
<Typography className={classes.statusEnabled}>
|
||||
<MdCheckCircle />
|
||||
{translate('resources.plugin.status.enabled')}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography className={classes.statusDisabled}>
|
||||
{translate('resources.plugin.status.disabled')}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
@ -239,9 +271,7 @@ const PluginShowContent = () => {
|
||||
? 'resources.plugin.actions.disable'
|
||||
: 'resources.plugin.actions.enable',
|
||||
)}
|
||||
labelPlacement="start"
|
||||
/>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -252,9 +282,16 @@ const PluginShowContent = () => {
|
||||
{translate('resources.plugin.sections.info')}
|
||||
</Typography>
|
||||
<dl className={classes.infoGrid}>
|
||||
<dt>{translate('resources.plugin.fields.name')}</dt>
|
||||
<dt>{translate('resources.plugin.fields.id')}</dt>
|
||||
<dd>{record.id}</dd>
|
||||
|
||||
{manifest?.name && (
|
||||
<>
|
||||
<dt>{translate('resources.plugin.fields.name')}</dt>
|
||||
<dd>{manifest.name}</dd>
|
||||
</>
|
||||
)}
|
||||
|
||||
{manifest?.version && (
|
||||
<>
|
||||
<dt>{translate('resources.plugin.fields.version')}</dt>
|
||||
@ -269,6 +306,42 @@ const PluginShowContent = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
{manifest?.author && (
|
||||
<>
|
||||
<dt>{translate('resources.plugin.fields.author')}</dt>
|
||||
<dd>{manifest.author}</dd>
|
||||
</>
|
||||
)}
|
||||
|
||||
{manifest?.website && (
|
||||
<>
|
||||
<dt>{translate('resources.plugin.fields.website')}</dt>
|
||||
<dd>
|
||||
<Link
|
||||
href={manifest.website}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{manifest.website}
|
||||
</Link>
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
|
||||
{manifest?.permissions && (
|
||||
<>
|
||||
<dt>{translate('resources.plugin.fields.permissions')}</dt>
|
||||
<dd className={classes.permissionsContainer}>
|
||||
<PermissionChip label="HTTP" permission={manifest.permissions.http} />
|
||||
<PermissionChip label="Subsonic API" permission={manifest.permissions.subsonicapi} />
|
||||
<PermissionChip label="Scheduler" permission={manifest.permissions.scheduler} />
|
||||
<PermissionChip label="WebSocket" permission={manifest.permissions.websocket} />
|
||||
<PermissionChip label="Artwork" permission={manifest.permissions.artwork} />
|
||||
<PermissionChip label="Cache" permission={manifest.permissions.cache} />
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
|
||||
<dt>{translate('resources.plugin.fields.path')}</dt>
|
||||
<dd className={classes.pathField}>{record.path}</dd>
|
||||
|
||||
@ -285,6 +358,20 @@ const PluginShowContent = () => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Manifest (Collapsible) */}
|
||||
<Accordion className={classes.section}>
|
||||
<AccordionSummary expandIcon={<MdExpandMore />}>
|
||||
<Typography variant="h6">
|
||||
{translate('resources.plugin.sections.manifest')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box className={classes.manifestBox} width="100%">
|
||||
{manifestJson}
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
{/* Configuration */}
|
||||
<Card className={classes.section}>
|
||||
<CardContent>
|
||||
@ -318,20 +405,6 @@ const PluginShowContent = () => {
|
||||
</Toolbar>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Manifest */}
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<MdExpandMore />}>
|
||||
<Typography variant="h6">
|
||||
{translate('resources.plugin.sections.manifest')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Box className={classes.manifestBox} width="100%">
|
||||
{manifestJson}
|
||||
</Box>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user