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",
|
"name": "Name",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
|
"author": "Author",
|
||||||
|
"website": "Website",
|
||||||
|
"permissions": "Permissions",
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
|
|||||||
@ -22,9 +22,12 @@ import {
|
|||||||
Accordion,
|
Accordion,
|
||||||
AccordionSummary,
|
AccordionSummary,
|
||||||
AccordionDetails,
|
AccordionDetails,
|
||||||
|
Chip,
|
||||||
|
Tooltip,
|
||||||
|
Link,
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
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 { Title, DateField } from '../common'
|
||||||
import { validateJson } from './jsonValidation'
|
import { validateJson } from './jsonValidation'
|
||||||
|
|
||||||
@ -71,15 +74,6 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
fontSize: '0.85rem',
|
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: {
|
toolbar: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
@ -104,8 +98,63 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
fontSize: '0.85rem',
|
fontSize: '0.85rem',
|
||||||
wordBreak: 'break-all',
|
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 PluginTitle = ({ record }) => {
|
||||||
const translate = useTranslate()
|
const translate = useTranslate()
|
||||||
const resourceName = translate('resources.plugin.name', { smart_count: 1 })
|
const resourceName = translate('resources.plugin.name', { smart_count: 1 })
|
||||||
@ -202,46 +251,27 @@ const PluginShowContent = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Status and Enable/Disable */}
|
{/* Status - Enable/Disable Switch Only */}
|
||||||
<Card className={classes.section}>
|
<Card className={classes.section}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography variant="h6" className={classes.sectionTitle}>
|
<Typography variant="h6" className={classes.sectionTitle}>
|
||||||
{translate('resources.plugin.sections.status')}
|
{translate('resources.plugin.sections.status')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<FormControlLabel
|
||||||
display="flex"
|
control={
|
||||||
alignItems="center"
|
<Switch
|
||||||
justifyContent="space-between"
|
checked={record.enabled}
|
||||||
>
|
onChange={handleToggleEnabled}
|
||||||
<Box>
|
disabled={loading}
|
||||||
{record.enabled ? (
|
color="primary"
|
||||||
<Typography className={classes.statusEnabled}>
|
/>
|
||||||
<MdCheckCircle />
|
}
|
||||||
{translate('resources.plugin.status.enabled')}
|
label={translate(
|
||||||
</Typography>
|
record.enabled
|
||||||
) : (
|
? 'resources.plugin.actions.disable'
|
||||||
<Typography className={classes.statusDisabled}>
|
: 'resources.plugin.actions.enable',
|
||||||
{translate('resources.plugin.status.disabled')}
|
)}
|
||||||
</Typography>
|
/>
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
checked={record.enabled}
|
|
||||||
onChange={handleToggleEnabled}
|
|
||||||
disabled={loading}
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={translate(
|
|
||||||
record.enabled
|
|
||||||
? 'resources.plugin.actions.disable'
|
|
||||||
: 'resources.plugin.actions.enable',
|
|
||||||
)}
|
|
||||||
labelPlacement="start"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -252,9 +282,16 @@ const PluginShowContent = () => {
|
|||||||
{translate('resources.plugin.sections.info')}
|
{translate('resources.plugin.sections.info')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<dl className={classes.infoGrid}>
|
<dl className={classes.infoGrid}>
|
||||||
<dt>{translate('resources.plugin.fields.name')}</dt>
|
<dt>{translate('resources.plugin.fields.id')}</dt>
|
||||||
<dd>{record.id}</dd>
|
<dd>{record.id}</dd>
|
||||||
|
|
||||||
|
{manifest?.name && (
|
||||||
|
<>
|
||||||
|
<dt>{translate('resources.plugin.fields.name')}</dt>
|
||||||
|
<dd>{manifest.name}</dd>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{manifest?.version && (
|
{manifest?.version && (
|
||||||
<>
|
<>
|
||||||
<dt>{translate('resources.plugin.fields.version')}</dt>
|
<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>
|
<dt>{translate('resources.plugin.fields.path')}</dt>
|
||||||
<dd className={classes.pathField}>{record.path}</dd>
|
<dd className={classes.pathField}>{record.path}</dd>
|
||||||
|
|
||||||
@ -285,6 +358,20 @@ const PluginShowContent = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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 */}
|
{/* Configuration */}
|
||||||
<Card className={classes.section}>
|
<Card className={classes.section}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@ -318,20 +405,6 @@ const PluginShowContent = () => {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</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>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user