scaffolder: Rework manage templates page

Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
Johan Haals
2024-09-26 09:40:57 +02:00
parent d02c467eed
commit 270d15ad07
4 changed files with 133 additions and 141 deletions
+5 -1
View File
@@ -274,9 +274,13 @@ export const scaffolderTranslationRef: TranslationRef<
readonly 'ongoingTask.showLogsButtonTitle': 'Show Logs';
readonly 'templateEditorForm.stepper.emptyText': 'There are no spec parameters in the template to preview.';
readonly 'templateTypePicker.title': 'Categories';
<<<<<<< HEAD:plugins/scaffolder/report-alpha.api.md
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';
>>>>>>> 0d22ec434749 (scaffolder: Rework manage templates page):plugins/scaffolder/api-report-alpha.md
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 +306,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.';
@@ -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 => {
+2 -2
View File
@@ -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.',
},