🔀 Merge branch 'dev' into feature/add-basic-authentication

This commit is contained in:
Manuel
2023-08-13 15:12:20 +02:00
202 changed files with 3334 additions and 1502 deletions

View File

@@ -1,105 +1,54 @@
import {
Button,
Container,
Group,
Text,
Title,
createStyles,
useMantineTheme,
} from '@mantine/core';
import { Button, Center, Stack, Text, Title, createStyles } from '@mantine/core';
import { GetServerSidePropsContext } from 'next';
import Head from 'next/head';
import Image from 'next/image';
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';
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() {
const { classes } = useStyles();
const { t } = useTranslation('layout/errors/not-found');
return (
<Container className={classes.root}>
<div className={classes.inner}>
<Illustration className={classes.image} />
<div className={classes.content}>
<Title className={classes.title}>Config not found</Title>
<Text color="dimmed" size="lg" align="center" className={classes.description}>
The config you are trying to access does not exist. Please check the URL and try again.
</Text>
<Group position="center">
<Link href="/">
<Button size="md">Take me back to home page</Button>
</Link>
</Group>
</div>
</div>
</Container>
<Center h="100dvh" w="100dvw">
<Head>
<title>Page not found Homarr</title>
</Head>
<Stack maw={500} p="xl">
<Image className={classes.image} src={pageNotFoundImage} width={200} height={200} alt="" />
<Title>{t('title')}</Title>
<Text>{t('text')}</Text>
<Button component={Link} variant="light" href="/">
{t('button')}
</Button>
</Stack>
</Center>
);
}
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 {
props: {
...translations,
},
};
}
const useStyles = createStyles(() => ({
image: {
margin: '0 auto',
display: 'blcok',
},
}));

View File

@@ -5,6 +5,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import Consola from 'consola';
import { getCookie, setCookie } from 'cookies-next';
import 'flag-icons/css/flag-icons.min.css';
import moment from 'moment-timezone';
import { GetServerSidePropsContext } from 'next';
import { Session } from 'next-auth';
import { SessionProvider, getSession } from 'next-auth/react';
@@ -16,6 +17,7 @@ import { CommonHead } from '~/components/layout/Meta/CommonHead';
import { env } from '~/env.js';
import { ColorSchemeProvider } from '~/hooks/use-colorscheme';
import { modals } from '~/modals';
import { getLanguageByCode } from '~/tools/language';
import { ConfigType } from '~/types/config';
import { api } from '~/utils/api';
import { colorSchemeParser } from '~/validations/user';
@@ -44,9 +46,15 @@ function App(
secondaryColor?: MantineTheme['primaryColor'];
primaryShade?: MantineTheme['primaryShade'];
session: Session;
configName?: string;
locale: string;
}>
) {
const { Component, pageProps } = props;
// TODO: make mapping from our locales to moment locales
const language = getLanguageByCode(pageProps.locale);
require('moment/locale/' + language.momentLocale);
moment.locale(language.momentLocale);
const [primaryColor, setPrimaryColor] = useState<MantineTheme['primaryColor']>(
props.pageProps.primaryColor ?? 'red'
@@ -151,6 +159,7 @@ App.getInitialProps = async ({ ctx }: { ctx: GetServerSidePropsContext }) => {
...getActiveColorScheme(session, ctx),
packageAttributes: getServiceSidePackageAttributes(),
session,
locale: ctx.locale ?? 'en',
},
};
};

83
src/pages/_error.tsx Normal file
View File

@@ -0,0 +1,83 @@
import {
Accordion,
Center,
Group,
Stack,
Text,
Title,
createStyles,
useMantineTheme,
} from '@mantine/core';
import { IconDeviceDesktop, IconInfoCircle, IconServer } from '@tabler/icons-react';
import { NextPageContext } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import imageBugFixing from '~/images/undraw_bug_fixing_oc-7-a.svg';
function Error({ statusCode }: { statusCode: number }) {
const { classes } = useStyles();
const theme = useMantineTheme();
const getColor = (color: string) => theme.colors[color][theme.colorScheme === 'dark' ? 5 : 7];
return (
<Center className={classes.root} h="100dvh" maw={400}>
<Head>
<title>An error occurred Homarr</title>
</Head>
<Stack>
<Image className={classes.image} src={imageBugFixing} alt="bug illustration" />
<Title>An unexpected error has occurred</Title>
<Text>
This page has crashed unexpectedly. We're sorry for the inconvenience. Please try again or
contact an administrator
</Text>
<Accordion variant="contained">
<Accordion.Item value="detailed">
<Accordion.Control icon={<IconInfoCircle color={getColor('red')} size="1rem" />}>
Detailed error information
</Accordion.Control>
<Accordion.Panel>
<Stack spacing="xs">
<Group position="apart">
<Text fw="bold">Type</Text>
<Text>
{statusCode ? (
<Group spacing="xs">
<IconServer size="1rem" />
<Text>Server side</Text>
</Group>
) : (
<Group spacing="xs">
<IconDeviceDesktop size="1rem" />
<Text>Client side</Text>
</Group>
)}
</Text>
</Group>
</Stack>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</Stack>
</Center>
);
}
Error.getInitialProps = ({ res, err }: NextPageContext) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
const useStyles = createStyles(() => ({
root: {
margin: '0 auto',
},
image: {
maxWidth: 400,
maxHeight: 200,
display: 'block',
margin: '0 auto',
},
}));
export default Error;