Merge pull request #2623 from BA1RY/feat/entity-404-page-2266

feat: 404 page for entity not found in catalog
This commit is contained in:
Ivan Shmidt
2020-09-29 15:23:36 +02:00
committed by GitHub
6 changed files with 157 additions and 13 deletions
@@ -0,0 +1,61 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { Grid, Button, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
import { Illo } from './Illo';
const useStyles = makeStyles<BackstageTheme>(theme => ({
container: {
paddingTop: theme.spacing(24),
paddingLeft: theme.spacing(8),
},
title: {
paddingBottom: theme.spacing(2),
},
body: {
paddingBottom: theme.spacing(6),
},
}));
export const EntityNotFound = () => {
const classes = useStyles();
return (
<Grid container className={classes.container}>
<Illo />
<Grid item xs={12} sm={6}>
<Typography variant="h2" className={classes.title}>
Entity was not found
</Typography>
<Typography variant="body1" className={classes.body}>
Want to help us build this? Check out our Getting Started
documentation.
</Typography>
<Button
variant="contained"
color="primary"
href="https://backstage.io/docs"
>
DOCS
</Button>
</Grid>
</Grid>
);
};
@@ -0,0 +1,33 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { makeStyles } from '@material-ui/core';
import IlloSvgUrl from './illo.svg';
const useStyles = makeStyles({
illo: {
maxWidth: '60%',
top: 100,
right: 20,
position: 'absolute',
},
});
export const Illo = () => {
const classes = useStyles();
return <img src={IlloSvgUrl} className={classes.illo} alt="" />;
};
@@ -0,0 +1,37 @@
<svg width="687" height="421" viewBox="0 0 687 421" fill="none"
xmlns="http://www.w3.org/2000/svg">
<rect x="16.231" y="114.353" width="407.223" height="48.4618" rx="24.2309" transform="rotate(-10.5556 16.231 114.353)" fill="#D9D9D9"/>
<rect x="56.5332" y="151.306" width="337.679" height="48.4618" rx="24.2309" transform="rotate(-10.5556 56.5332 151.306)" fill="#D9D9D9"/>
<rect x="54.707" y="274.403" width="441.061" height="48.4618" rx="24.2309" transform="rotate(-10.5556 54.707 274.403)" fill="#D9D9D9"/>
<rect x="38.1221" y="321.956" width="440.421" height="48.4618" rx="24.2309" transform="rotate(-10.5556 38.1221 321.956)" fill="#D9D9D9"/>
<rect x="213.923" y="147.109" width="435.885" height="48.4618" rx="24.2309" transform="rotate(-10.5556 213.923 147.109)" fill="#D9D9D9"/>
<rect x="199.842" y="194.196" width="431.054" height="48.4618" rx="24.2309" transform="rotate(-10.5556 199.842 194.196)" fill="#D9D9D9"/>
<rect x="235.299" y="292.946" width="394.936" height="48.4618" rx="24.2309" transform="rotate(-10.5556 235.299 292.946)" fill="#D9D9D9"/>
<rect x="199.862" y="344.013" width="456.282" height="48.4618" rx="24.2309" transform="rotate(-10.5556 199.862 344.013)" fill="#D9D9D9"/>
<g filter="url(#filter0_d)">
<rect x="126" y="67" width="461" height="286" rx="10" fill="#F8F8F8"/>
<rect x="154" y="91" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="154" y="175.354" width="90" height="69.2911" fill="#D9D9D9"/>
<rect x="154" y="261.717" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="259" y="91" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="364" y="91" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="364" y="175.354" width="90" height="69.2911" fill="#EEEEEE"/>
<rect x="364" y="261.717" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="259" y="175.354" width="90" height="69.2911" fill="#D9D9D9"/>
<rect x="259" y="261.717" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="469" y="91" width="90" height="67.2827" fill="#D9D9D9"/>
<rect x="469" y="175.354" width="90" height="69.2911" fill="#D9D9D9"/>
<rect x="469" y="261.717" width="90" height="67.2827" fill="#D9D9D9"/>
</g>
<defs>
<filter id="filter0_d" x="116" y="59" width="485" height="310" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
<feOffset dx="2" dy="4"/>
<feGaussianBlur stdDeviation="6"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1,16 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { EntityNotFound } from './EntityNotFound';
+4 -2
View File
@@ -15,6 +15,7 @@
*/
import React, { ComponentType } from 'react';
import { CatalogPage } from './CatalogPage';
import { EntityNotFound } from './EntityNotFound';
import { EntityPageLayout } from './EntityPageLayout';
import { Route, Routes } from 'react-router';
import { entityRoute, rootRoute } from '../routes';
@@ -45,9 +46,10 @@ const DefaultEntityPage = () => (
);
const EntityPageSwitch = ({ EntityPage }: { EntityPage: ComponentType }) => {
const { entity } = useEntity();
const { entity, loading, error } = useEntity();
// Loading and error states
if (!entity) return <EntityPageLayout />;
if (loading) return <EntityPageLayout />;
if (error || (!loading && !entity)) return <EntityNotFound />;
// Otherwise EntityPage provided from the App
// Note that EntityPage will include EntityPageLayout already
+6 -11
View File
@@ -20,8 +20,6 @@ import { catalogApiRef } from '../api/types';
import { useAsync } from 'react-use';
import { Entity } from '@backstage/catalog-model';
const REDIRECT_DELAY = 2000;
type EntityLoadingStatus = {
entity?: Entity;
loading: boolean;
@@ -47,13 +45,6 @@ export const useEntityFromUrl = (): EntityLoadingStatus => {
);
useEffect(() => {
if (error || (!loading && !entity)) {
errorApi.post(new Error('Entity not found!'));
setTimeout(() => {
navigate('/');
}, REDIRECT_DELAY);
}
if (!name) {
errorApi.post(new Error('No name provided!'));
navigate('/');
@@ -67,6 +58,10 @@ export const useEntityFromUrl = (): EntityLoadingStatus => {
* Always going to return an entity, or throw an error if not a descendant of a EntityProvider.
*/
export const useEntity = () => {
const { entity } = useContext<{ entity: Entity }>(EntityContext as any);
return { entity };
const { entity, loading, error } = useContext<{
entity: Entity;
loading: boolean;
error: Error;
}>(EntityContext as any);
return { entity, loading, error };
};