Merge pull request #26868 from backstage/jhaals/editpage
scaffolder: Update manage template page layout
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': minor
|
||||
---
|
||||
|
||||
Improved the layout of the manage templates page (`/edit`) by adding icons and descriptions that better describe what each page is for. Updated the header menu to link back to the scaffolder create page.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': minor
|
||||
---
|
||||
|
||||
Renamed Template Editor to Manage Templates.
|
||||
+1
-1
@@ -105,7 +105,7 @@ export function ScaffolderPageContextMenu(
|
||||
<ListItemIcon>
|
||||
<Edit fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Template Editor" />
|
||||
<ListItemText primary="Manage Templates" />
|
||||
</MenuItem>
|
||||
)}
|
||||
{onActionsClicked && (
|
||||
|
||||
@@ -276,7 +276,7 @@ export const scaffolderTranslationRef: TranslationRef<
|
||||
readonly 'templateTypePicker.title': 'Categories';
|
||||
readonly 'templateFormPage.title': 'Template Form Playground';
|
||||
readonly 'templateFormPage.subtitle': 'Edit, preview, and try out templates and template forms';
|
||||
readonly 'templateEditorPage.title': 'Template Editor';
|
||||
readonly 'templateEditorPage.title': 'Manage Templates';
|
||||
readonly 'templateEditorPage.subtitle': 'Edit, preview, and try out templates and template forms';
|
||||
readonly 'templateEditorPage.dryRunResults.title': 'Dry-run results';
|
||||
readonly 'templateEditorPage.dryRunResultsList.title': 'Result {{resultId}}';
|
||||
@@ -302,7 +302,7 @@ export const scaffolderTranslationRef: TranslationRef<
|
||||
readonly 'templateEditorPage.templateEditorIntro.createLocal.title': 'Create New Template';
|
||||
readonly 'templateEditorPage.templateEditorIntro.createLocal.description': 'Create a local template directory, allowing you to both edit and try executing your own template.';
|
||||
readonly 'templateEditorPage.templateEditorIntro.createLocal.unsupportedTooltip': 'Only supported in some Chromium-based browsers';
|
||||
readonly 'templateEditorPage.templateEditorIntro.formEditor.title': 'Edit Template Form';
|
||||
readonly 'templateEditorPage.templateEditorIntro.formEditor.title': 'Template playground';
|
||||
readonly 'templateEditorPage.templateEditorIntro.formEditor.description': 'Preview and edit a template form, either using a sample template or by loading a template from the catalog.';
|
||||
readonly 'templateEditorPage.templateEditorIntro.fieldExplorer.title': 'Custom Field Explorer';
|
||||
readonly 'templateEditorPage.templateEditorIntro.fieldExplorer.description': 'View and play around with available installed custom field extensions.';
|
||||
|
||||
+123
-123
@@ -18,30 +18,63 @@ import React from 'react';
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardActionArea from '@material-ui/core/CardActionArea';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { WebFileSystemAccess } from '../../../lib/filesystem';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { scaffolderTranslationRef } from '../../../translation';
|
||||
import CreateNewFolderIcon from '@material-ui/icons/CreateNewFolder';
|
||||
import ListAltIcon from '@material-ui/icons/ListAlt';
|
||||
import FormatListBulletedIcon from '@material-ui/icons/FormatListBulleted';
|
||||
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
|
||||
import CardMedia from '@material-ui/core/CardMedia';
|
||||
import PublishIcon from '@material-ui/icons/Publish';
|
||||
import SvgIcon from '@material-ui/core/SvgIcon';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
gridRoot: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
cardGrid: {
|
||||
display: 'grid',
|
||||
maxWidth: 1000,
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gridAutoRows: '1fr 1fr',
|
||||
gap: '1rem',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
gridAutoFlow: 'row',
|
||||
},
|
||||
},
|
||||
card: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'auto 1fr',
|
||||
gridTemplateRows: '1fr',
|
||||
alignItems: 'center',
|
||||
margin: theme.spacing(0, 1),
|
||||
marginTop: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
icon: {
|
||||
justifySelf: 'center',
|
||||
paddingTop: theme.spacing(1),
|
||||
fontSize: 48,
|
||||
},
|
||||
introText: {
|
||||
textAlign: 'center',
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
card: {
|
||||
position: 'relative',
|
||||
maxWidth: 340,
|
||||
marginTop: theme.spacing(4),
|
||||
margin: theme.spacing(0, 2),
|
||||
},
|
||||
infoIcon: {
|
||||
position: 'absolute',
|
||||
top: theme.spacing(1),
|
||||
right: theme.spacing(1),
|
||||
},
|
||||
cardContent: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
interface EditorIntroProps {
|
||||
@@ -51,142 +84,109 @@ interface EditorIntroProps {
|
||||
) => void;
|
||||
}
|
||||
|
||||
export function TemplateEditorIntro(props: EditorIntroProps) {
|
||||
const classes = useStyles();
|
||||
const supportsLoad = WebFileSystemAccess.isSupported();
|
||||
function ActionCard(props: {
|
||||
title: string;
|
||||
description: string;
|
||||
Icon: typeof SvgIcon;
|
||||
action?: React.MouseEventHandler;
|
||||
requireLoad?: boolean;
|
||||
}) {
|
||||
const supportsLoad = props.requireLoad
|
||||
? WebFileSystemAccess.isSupported()
|
||||
: true;
|
||||
const { t } = useTranslationRef(scaffolderTranslationRef);
|
||||
|
||||
const cardLoadLocal = (
|
||||
<Card className={classes.card} elevation={4}>
|
||||
<CardActionArea
|
||||
disabled={!supportsLoad}
|
||||
onClick={() => props.onSelect?.('local')}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h3"
|
||||
gutterBottom
|
||||
color={supportsLoad ? undefined : 'textSecondary'}
|
||||
style={{ display: 'flex', flexFlow: 'row nowrap' }}
|
||||
>
|
||||
{t('templateEditorPage.templateEditorIntro.loadLocal.title')}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
color={supportsLoad ? undefined : 'textSecondary'}
|
||||
>
|
||||
{t('templateEditorPage.templateEditorIntro.loadLocal.description')}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
const classes = useStyles();
|
||||
const { Icon, title, description, action } = props;
|
||||
return (
|
||||
<Card className={classes.card}>
|
||||
{!supportsLoad && (
|
||||
<div className={classes.infoIcon}>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title={t(
|
||||
'templateEditorPage.templateEditorIntro.loadLocal.unsupportedTooltip',
|
||||
)}
|
||||
>
|
||||
<InfoOutlinedIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title={t(
|
||||
'templateEditorPage.templateEditorIntro.loadLocal.unsupportedTooltip',
|
||||
)}
|
||||
>
|
||||
<InfoOutlinedIcon />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
|
||||
const cardCreateLocal = (
|
||||
<Card className={classes.card} elevation={4}>
|
||||
<CardActionArea
|
||||
disabled={!supportsLoad}
|
||||
onClick={() => props.onSelect?.('create-template')}
|
||||
>
|
||||
<CardContent>
|
||||
<CardActionArea onClick={action}>
|
||||
<CardMedia>
|
||||
<Icon
|
||||
className={classes.icon}
|
||||
color={supportsLoad ? undefined : 'disabled'}
|
||||
/>
|
||||
</CardMedia>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h3"
|
||||
gutterBottom
|
||||
color={supportsLoad ? undefined : 'textSecondary'}
|
||||
style={{ display: 'flex', flexFlow: 'row nowrap' }}
|
||||
>
|
||||
{t('templateEditorPage.templateEditorIntro.createLocal.title')}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
variant="h5"
|
||||
component="h2"
|
||||
color={supportsLoad ? undefined : 'textSecondary'}
|
||||
>
|
||||
{t(
|
||||
'templateEditorPage.templateEditorIntro.createLocal.description',
|
||||
)}
|
||||
{title}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
{!supportsLoad && (
|
||||
<div className={classes.infoIcon}>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title={t(
|
||||
'templateEditorPage.templateEditorIntro.createLocal.unsupportedTooltip',
|
||||
)}
|
||||
>
|
||||
<InfoOutlinedIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
|
||||
const cardFormEditor = (
|
||||
<Card className={classes.card} elevation={4}>
|
||||
<CardActionArea onClick={() => props.onSelect?.('form')}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" component="h3" gutterBottom>
|
||||
{t('templateEditorPage.templateEditorIntro.formEditor.title')}
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{t('templateEditorPage.templateEditorIntro.formEditor.description')}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const cardFieldExplorer = (
|
||||
<Card className={classes.card} elevation={4}>
|
||||
<CardActionArea onClick={() => props.onSelect?.('field-explorer')}>
|
||||
<CardContent>
|
||||
<Typography variant="h4" component="h3" gutterBottom>
|
||||
{t('templateEditorPage.templateEditorIntro.fieldExplorer.title')}
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
{t(
|
||||
'templateEditorPage.templateEditorIntro.fieldExplorer.description',
|
||||
)}
|
||||
<Typography variant="body2" color="textSecondary" component="p">
|
||||
{description}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
export function TemplateEditorIntro(props: EditorIntroProps) {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslationRef(scaffolderTranslationRef);
|
||||
|
||||
return (
|
||||
<div style={props.style}>
|
||||
<Typography variant="h4" component="h2" className={classes.introText}>
|
||||
{t('templateEditorPage.templateEditorIntro.title')}
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexFlow: 'row wrap',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
alignContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
{supportsLoad && cardLoadLocal}
|
||||
{supportsLoad && cardCreateLocal}
|
||||
{cardFormEditor}
|
||||
{!supportsLoad && cardLoadLocal}
|
||||
{cardFieldExplorer}
|
||||
<div className={classes.gridRoot}>
|
||||
<div className={classes.cardGrid}>
|
||||
<ActionCard
|
||||
title={t('templateEditorPage.templateEditorIntro.loadLocal.title')}
|
||||
description={t(
|
||||
'templateEditorPage.templateEditorIntro.loadLocal.description',
|
||||
)}
|
||||
requireLoad
|
||||
Icon={PublishIcon}
|
||||
action={() => props.onSelect?.('local')}
|
||||
/>
|
||||
<ActionCard
|
||||
title={t(
|
||||
'templateEditorPage.templateEditorIntro.createLocal.title',
|
||||
)}
|
||||
description={t(
|
||||
'templateEditorPage.templateEditorIntro.createLocal.description',
|
||||
)}
|
||||
requireLoad
|
||||
action={() => props.onSelect?.('create-template')}
|
||||
Icon={CreateNewFolderIcon}
|
||||
/>
|
||||
|
||||
<ActionCard
|
||||
title={t(
|
||||
'templateEditorPage.templateEditorIntro.fieldExplorer.title',
|
||||
)}
|
||||
description={t(
|
||||
'templateEditorPage.templateEditorIntro.fieldExplorer.description',
|
||||
)}
|
||||
Icon={FormatListBulletedIcon}
|
||||
action={() => props.onSelect?.('field-explorer')}
|
||||
/>
|
||||
|
||||
<ActionCard
|
||||
title={t('templateEditorPage.templateEditorIntro.formEditor.title')}
|
||||
description={t(
|
||||
'templateEditorPage.templateEditorIntro.formEditor.description',
|
||||
)}
|
||||
Icon={ListAltIcon}
|
||||
action={() => props.onSelect?.('form')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,15 +19,12 @@ import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { WebFileSystemAccess } from '../../../lib/filesystem';
|
||||
|
||||
import { TemplateEditorIntro } from './TemplateEditorIntro';
|
||||
import { ScaffolderPageContextMenu } from '@backstage/plugin-scaffolder-react/alpha';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
actionsRouteRef,
|
||||
editorRouteRef,
|
||||
customFieldsRouteRef,
|
||||
rootRouteRef,
|
||||
scaffolderListTaskRouteRef,
|
||||
templateFormRouteRef,
|
||||
} from '../../../routes';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
@@ -37,29 +34,20 @@ import { createExampleTemplate } from '../../../lib/filesystem/createExampleTemp
|
||||
|
||||
export function TemplateEditorPage() {
|
||||
const navigate = useNavigate();
|
||||
const actionsLink = useRouteRef(actionsRouteRef);
|
||||
const tasksLink = useRouteRef(scaffolderListTaskRouteRef);
|
||||
const createLink = useRouteRef(rootRouteRef);
|
||||
const editorLink = useRouteRef(editorRouteRef);
|
||||
const customFieldsLink = useRouteRef(customFieldsRouteRef);
|
||||
const templateFormLink = useRouteRef(templateFormRouteRef);
|
||||
const { t } = useTranslationRef(scaffolderTranslationRef);
|
||||
|
||||
const scaffolderPageContextMenuProps = {
|
||||
onEditorClicked: undefined,
|
||||
onActionsClicked: () => navigate(actionsLink()),
|
||||
onTasksClicked: () => navigate(tasksLink()),
|
||||
onCreateClicked: () => navigate(createLink()),
|
||||
};
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header
|
||||
title={t('templateEditorPage.title')}
|
||||
type="Scaffolder"
|
||||
typeLink={createLink()}
|
||||
subtitle={t('templateEditorPage.subtitle')}
|
||||
>
|
||||
<ScaffolderPageContextMenu {...scaffolderPageContextMenuProps} />
|
||||
</Header>
|
||||
/>
|
||||
<Content>
|
||||
<TemplateEditorIntro
|
||||
onSelect={option => {
|
||||
|
||||
@@ -199,7 +199,7 @@ export const scaffolderTranslationRef = createTranslationRef({
|
||||
subtitle: 'Edit, preview, and try out templates and template forms',
|
||||
},
|
||||
templateEditorPage: {
|
||||
title: 'Template Editor',
|
||||
title: 'Manage Templates',
|
||||
subtitle: 'Edit, preview, and try out templates and template forms',
|
||||
dryRunResults: {
|
||||
title: 'Dry-run results',
|
||||
@@ -253,7 +253,7 @@ export const scaffolderTranslationRef = createTranslationRef({
|
||||
unsupportedTooltip: 'Only supported in some Chromium-based browsers',
|
||||
},
|
||||
formEditor: {
|
||||
title: 'Edit Template Form',
|
||||
title: 'Template playground',
|
||||
description:
|
||||
'Preview and edit a template form, either using a sample template or by loading a template from the catalog.',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user