mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-18 11:11:10 +01:00
🥅 Add 404 error page
This commit is contained in:
5
public/locales/en/layout/errors/not-found.json
Normal file
5
public/locales/en/layout/errors/not-found.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"title": "Page not found",
|
||||||
|
"text": "This page could not be found. The URL for this page may have expired, the URL is invalid or you do now have the required permissions to access this resource.",
|
||||||
|
"button": "Go to Home"
|
||||||
|
}
|
||||||
1
src/images/undraw_page_not_found_re_e9o6.svg
Normal file
1
src/images/undraw_page_not_found_re_e9o6.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
@@ -1,105 +1,54 @@
|
|||||||
import {
|
import { Button, Center, Stack, Text, Title, createStyles } from '@mantine/core';
|
||||||
Button,
|
|
||||||
Container,
|
|
||||||
Group,
|
|
||||||
Text,
|
|
||||||
Title,
|
|
||||||
createStyles,
|
|
||||||
useMantineTheme,
|
|
||||||
} from '@mantine/core';
|
|
||||||
import { GetServerSidePropsContext } from 'next';
|
import { GetServerSidePropsContext } from 'next';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import React from 'react';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import pageNotFoundImage from '~/images/undraw_page_not_found_re_e9o6.svg';
|
||||||
|
import { pageNotFoundNamespaces } from '~/tools/server/translation-namespaces';
|
||||||
|
|
||||||
import { getServerSideTranslations } from '../tools/server/getServerSideTranslations';
|
import { getServerSideTranslations } from '../tools/server/getServerSideTranslations';
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
|
||||||
root: {
|
|
||||||
paddingTop: 80,
|
|
||||||
paddingBottom: 80,
|
|
||||||
},
|
|
||||||
|
|
||||||
inner: {
|
|
||||||
position: 'relative',
|
|
||||||
},
|
|
||||||
|
|
||||||
image: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 0,
|
|
||||||
opacity: 0.75,
|
|
||||||
},
|
|
||||||
|
|
||||||
content: {
|
|
||||||
paddingTop: 220,
|
|
||||||
position: 'relative',
|
|
||||||
zIndex: 1,
|
|
||||||
|
|
||||||
[theme.fn.smallerThan('sm')]: {
|
|
||||||
paddingTop: 120,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
title: {
|
|
||||||
fontFamily: `Greycliff CF, ${theme.fontFamily}`,
|
|
||||||
textAlign: 'center',
|
|
||||||
fontWeight: 900,
|
|
||||||
fontSize: 38,
|
|
||||||
|
|
||||||
[theme.fn.smallerThan('sm')]: {
|
|
||||||
fontSize: 32,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
description: {
|
|
||||||
maxWidth: 540,
|
|
||||||
margin: 'auto',
|
|
||||||
marginTop: theme.spacing.xl,
|
|
||||||
marginBottom: `calc(${theme.spacing.xl} * 1.5)`,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
function Illustration(props: React.ComponentPropsWithoutRef<'svg'>) {
|
|
||||||
const theme = useMantineTheme();
|
|
||||||
return (
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 362 145" {...props}>
|
|
||||||
<path
|
|
||||||
fill={theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0]}
|
|
||||||
d="M62.6 142c-2.133 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2L58.2 4c.8-1.333 2.067-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .667.533 1 1.267 1 2.2v21.2c0 .933-.333 1.733-1 2.4-.667.533-1.467.8-2.4.8H93v20.8c0 2.133-1.067 3.2-3.2 3.2H62.6zM33 90.4h26.4V51.2L33 90.4zM181.67 144.6c-7.333 0-14.333-1.333-21-4-6.666-2.667-12.866-6.733-18.6-12.2-5.733-5.467-10.266-13-13.6-22.6-3.333-9.6-5-20.667-5-33.2 0-12.533 1.667-23.6 5-33.2 3.334-9.6 7.867-17.133 13.6-22.6 5.734-5.467 11.934-9.533 18.6-12.2 6.667-2.8 13.667-4.2 21-4.2 7.467 0 14.534 1.4 21.2 4.2 6.667 2.667 12.8 6.733 18.4 12.2 5.734 5.467 10.267 13 13.6 22.6 3.334 9.6 5 20.667 5 33.2 0 12.533-1.666 23.6-5 33.2-3.333 9.6-7.866 17.133-13.6 22.6-5.6 5.467-11.733 9.533-18.4 12.2-6.666 2.667-13.733 4-21.2 4zm0-31c9.067 0 15.6-3.733 19.6-11.2 4.134-7.6 6.2-17.533 6.2-29.8s-2.066-22.2-6.2-29.8c-4.133-7.6-10.666-11.4-19.6-11.4-8.933 0-15.466 3.8-19.6 11.4-4 7.6-6 17.533-6 29.8s2 22.2 6 29.8c4.134 7.467 10.667 11.2 19.6 11.2zM316.116 142c-2.134 0-3.2-1.067-3.2-3.2V118h-56c-2 0-3-1-3-3V92.8c0-1.333.4-2.733 1.2-4.2l56.6-84.6c.8-1.333 2.066-2 3.8-2h28c2 0 3 1 3 3v85.4h11.2c.933 0 1.733.333 2.4 1 .666.533 1 1.267 1 2.2v21.2c0 .933-.334 1.733-1 2.4-.667.533-1.467.8-2.4.8h-11.2v20.8c0 2.133-1.067 3.2-3.2 3.2h-27.2zm-29.6-51.6h26.4V51.2l-26.4 39.2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
|
const { t } = useTranslation('layout/errors/not-found');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={classes.root}>
|
<Center h="100dvh" w="100dvw">
|
||||||
<div className={classes.inner}>
|
<Head>
|
||||||
<Illustration className={classes.image} />
|
<title>Page not found • Homarr</title>
|
||||||
<div className={classes.content}>
|
</Head>
|
||||||
<Title className={classes.title}>Config not found</Title>
|
<Stack maw={500} p="xl">
|
||||||
<Text color="dimmed" size="lg" align="center" className={classes.description}>
|
<Image className={classes.image} src={pageNotFoundImage} width={200} height={200} alt="" />
|
||||||
The config you are trying to access does not exist. Please check the URL and try again.
|
<Title>{t('title')}</Title>
|
||||||
</Text>
|
<Text>{t('text')}</Text>
|
||||||
<Group position="center">
|
|
||||||
<Link href="/">
|
<Button component={Link} variant="light" href="/">
|
||||||
<Button size="md">Take me back to home page</Button>
|
{t('button')}
|
||||||
</Link>
|
</Button>
|
||||||
</Group>
|
</Stack>
|
||||||
</div>
|
</Center>
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticProps({ req, res, locale }: GetServerSidePropsContext) {
|
export async function getStaticProps({ req, res, locale }: GetServerSidePropsContext) {
|
||||||
const translations = await getServerSideTranslations(['common'], locale, undefined, undefined);
|
const translations = await getServerSideTranslations(
|
||||||
|
[...pageNotFoundNamespaces, 'common'],
|
||||||
|
locale,
|
||||||
|
req,
|
||||||
|
res
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
...translations,
|
...translations,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useStyles = createStyles(() => ({
|
||||||
|
image: {
|
||||||
|
margin: '0 auto',
|
||||||
|
display: 'blcok',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|||||||
@@ -49,3 +49,5 @@ export const dashboardNamespaces = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const loginNamespaces = ['authentication/login'];
|
export const loginNamespaces = ['authentication/login'];
|
||||||
|
|
||||||
|
export const pageNotFoundNamespaces = ['layout/errors/not-found'];
|
||||||
|
|||||||
Reference in New Issue
Block a user