Show a nicer error message when server is installing
This commit is contained in:
parent
1aa3e0fb63
commit
e044e8db1c
12 changed files with 117 additions and 34 deletions
3
public/.gitignore
vendored
3
public/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
assets/*
|
assets/*
|
||||||
!assets/*.svg
|
!assets/svgs
|
||||||
|
!assets/svgs/*.svg
|
||||||
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
1
public/assets/svgs/server_error.svg
Normal file
1
public/assets/svgs/server_error.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.4 KiB |
1
public/assets/svgs/server_installing.svg
Normal file
1
public/assets/svgs/server_installing.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 23 KiB |
|
@ -1,44 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route } from 'react-router';
|
import { Route } from 'react-router';
|
||||||
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
||||||
import styled from 'styled-components';
|
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||||
import { breakpoint } from 'styled-components-breakpoint';
|
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const ContentContainer = styled.div`
|
|
||||||
max-width: 1200px;
|
|
||||||
${tw`mx-4`};
|
|
||||||
|
|
||||||
${breakpoint('xl')`
|
|
||||||
${tw`mx-auto`};
|
|
||||||
`};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default ({ children }: Props) => (
|
export default ({ children }: Props) => (
|
||||||
<Route
|
<Route
|
||||||
render={({ location }) => (
|
render={({ location }) => (
|
||||||
<TransitionGroup className={'route-transition-group'}>
|
<TransitionGroup className={'route-transition-group'}>
|
||||||
<CSSTransition key={location.key} timeout={250} in={true} appear={true} classNames={'fade'}>
|
<CSSTransition key={location.key} timeout={250} in={true} appear={true} classNames={'fade'}>
|
||||||
<section>
|
<section>
|
||||||
<ContentContainer>
|
<PageContentBlock>
|
||||||
{children}
|
{children}
|
||||||
</ContentContainer>
|
</PageContentBlock>
|
||||||
<ContentContainer className={'mb-4'}>
|
|
||||||
<p className={'text-right text-neutral-500 text-xs'}>
|
|
||||||
© 2015 - 2020
|
|
||||||
<a
|
|
||||||
rel={'noopener nofollow'}
|
|
||||||
href={'https://pterodactyl.io'}
|
|
||||||
target={'_blank'}
|
|
||||||
className={'no-underline text-neutral-500 hover:text-neutral-300'}
|
|
||||||
>
|
|
||||||
Pterodactyl Software
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</ContentContainer>
|
|
||||||
</section>
|
</section>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
|
@ -25,8 +25,10 @@ http.interceptors.response.use(resp => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
}, () => {
|
}, error => {
|
||||||
store.getActions().progress.setComplete();
|
store.getActions().progress.setComplete();
|
||||||
|
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we have a phpdebugbar instance registered at this point in time go
|
// If we have a phpdebugbar instance registered at this point in time go
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default forwardRef<HTMLFormElement, Props>(({ title, ...props }, ref) =>
|
||||||
<Form {...props} ref={ref}>
|
<Form {...props} ref={ref}>
|
||||||
<div className={'md:flex w-full bg-white shadow-lg rounded-lg p-6 md:pl-0 mx-1'}>
|
<div className={'md:flex w-full bg-white shadow-lg rounded-lg p-6 md:pl-0 mx-1'}>
|
||||||
<div className={'flex-none select-none mb-6 md:mb-0 self-center'}>
|
<div className={'flex-none select-none mb-6 md:mb-0 self-center'}>
|
||||||
<img src={'/assets/pterodactyl.svg'} className={'block w-48 md:w-64 mx-auto'}/>
|
<img src={'/assets/svgs/pterodactyl.svg'} className={'block w-48 md:w-64 mx-auto'}/>
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex-1'}>
|
<div className={'flex-1'}>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|
13
resources/scripts/components/elements/ContentContainer.tsx
Normal file
13
resources/scripts/components/elements/ContentContainer.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { breakpoint } from 'styled-components-breakpoint';
|
||||||
|
|
||||||
|
const ContentContainer = styled.div`
|
||||||
|
max-width: 1200px;
|
||||||
|
${tw`mx-4`};
|
||||||
|
|
||||||
|
${breakpoint('xl')`
|
||||||
|
${tw`mx-auto`};
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default ContentContainer;
|
30
resources/scripts/components/elements/PageContentBlock.tsx
Normal file
30
resources/scripts/components/elements/PageContentBlock.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ContentContainer from '@/components/elements/ContentContainer';
|
||||||
|
import { CSSTransition } from 'react-transition-group';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({ children }: Props) => (
|
||||||
|
<CSSTransition timeout={250} classNames={'fade'} appear={true} in={true}>
|
||||||
|
<>
|
||||||
|
<ContentContainer className={'my-10'}>
|
||||||
|
{children}
|
||||||
|
</ContentContainer>
|
||||||
|
<ContentContainer className={'mb-4'}>
|
||||||
|
<p className={'text-right text-neutral-500 text-xs'}>
|
||||||
|
© 2015 - 2020
|
||||||
|
<a
|
||||||
|
rel={'noopener nofollow'}
|
||||||
|
href={'https://pterodactyl.io'}
|
||||||
|
target={'_blank'}
|
||||||
|
className={'no-underline text-neutral-500 hover:text-neutral-300'}
|
||||||
|
>
|
||||||
|
Pterodactyl Software
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</ContentContainer>
|
||||||
|
</>
|
||||||
|
</CSSTransition>
|
||||||
|
);
|
21
resources/scripts/components/screens/ServerError.tsx
Normal file
21
resources/scripts/components/screens/ServerError.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({ title, message }: Props) => (
|
||||||
|
<PageContentBlock>
|
||||||
|
<div className={'flex justify-center'}>
|
||||||
|
<div className={'w-full sm:w-3/4 md:w-1/2 p-12 md:p-20 bg-neutral-100 rounded-lg shadow-lg text-center'}>
|
||||||
|
<img src={'/assets/svgs/server_error.svg'} className={'w-2/3 h-auto select-none'}/>
|
||||||
|
<h2 className={'mt-6 text-neutral-900 font-bold'}>{ title || 'Something went wrong!' }</h2>
|
||||||
|
<p className={'text-sm text-neutral-700 mt-2'}>
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageContentBlock>
|
||||||
|
);
|
16
resources/scripts/components/screens/ServerInstalling.tsx
Normal file
16
resources/scripts/components/screens/ServerInstalling.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PageContentBlock from '@/components/elements/PageContentBlock';
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<PageContentBlock>
|
||||||
|
<div className={'flex justify-center'}>
|
||||||
|
<div className={'w-full sm:w-3/4 md:w-1/2 p-12 md:p-20 bg-neutral-100 rounded-lg shadow-lg text-center'}>
|
||||||
|
<img src={'/assets/svgs/server_installing.svg'} className={'w-2/3 h-auto select-none'}/>
|
||||||
|
<h2 className={'mt-6 text-neutral-900 font-bold'}>Your server is installing.</h2>
|
||||||
|
<p className={'text-sm text-neutral-700 mt-2'}>
|
||||||
|
Please check back in a few minutes.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageContentBlock>
|
||||||
|
);
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
import { NavLink, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||||
import NavigationBar from '@/components/NavigationBar';
|
import NavigationBar from '@/components/NavigationBar';
|
||||||
import ServerConsole from '@/components/server/ServerConsole';
|
import ServerConsole from '@/components/server/ServerConsole';
|
||||||
|
@ -17,8 +17,13 @@ import UsersContainer from '@/components/server/users/UsersContainer';
|
||||||
import Can from '@/components/elements/Can';
|
import Can from '@/components/elements/Can';
|
||||||
import BackupContainer from '@/components/server/backups/BackupContainer';
|
import BackupContainer from '@/components/server/backups/BackupContainer';
|
||||||
import Spinner from '@/components/elements/Spinner';
|
import Spinner from '@/components/elements/Spinner';
|
||||||
|
import ServerInstalling from '@/components/screens/ServerInstalling';
|
||||||
|
import ServerError from '@/components/screens/ServerError';
|
||||||
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
|
|
||||||
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
|
const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>) => {
|
||||||
|
const [ error, setError ] = useState('');
|
||||||
|
const [ installing, setInstalling ] = useState(false);
|
||||||
const server = ServerContext.useStoreState(state => state.server.data);
|
const server = ServerContext.useStoreState(state => state.server.data);
|
||||||
const getServer = ServerContext.useStoreActions(actions => actions.server.getServer);
|
const getServer = ServerContext.useStoreActions(actions => actions.server.getServer);
|
||||||
const clearServerState = ServerContext.useStoreActions(actions => actions.clearServerState);
|
const clearServerState = ServerContext.useStoreActions(actions => actions.clearServerState);
|
||||||
|
@ -28,7 +33,17 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getServer(match.params.id);
|
setError('');
|
||||||
|
setInstalling(false);
|
||||||
|
getServer(match.params.id)
|
||||||
|
.catch(error => {
|
||||||
|
if (error.response?.status === 409) {
|
||||||
|
setInstalling(true);
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
setError(httpErrorToHuman(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearServerState();
|
clearServerState();
|
||||||
|
@ -39,9 +54,15 @@ const ServerRouter = ({ match, location }: RouteComponentProps<{ id: string }>)
|
||||||
<React.Fragment key={'server-router'}>
|
<React.Fragment key={'server-router'}>
|
||||||
<NavigationBar/>
|
<NavigationBar/>
|
||||||
{!server ?
|
{!server ?
|
||||||
<div className={'flex justify-center m-20'}>
|
!installing ?
|
||||||
<Spinner size={'large'}/>
|
error ?
|
||||||
</div>
|
<ServerError message={error}/>
|
||||||
|
:
|
||||||
|
<div className={'flex justify-center m-20'}>
|
||||||
|
<Spinner size={'large'}/>
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<ServerInstalling/>
|
||||||
:
|
:
|
||||||
<>
|
<>
|
||||||
<CSSTransition timeout={250} classNames={'fade'} appear={true} in={true}>
|
<CSSTransition timeout={250} classNames={'fade'} appear={true} in={true}>
|
||||||
|
|
Loading…
Reference in a new issue