Get settings page in working order

This commit is contained in:
Dane Everitt 2020-07-04 15:58:14 -07:00
parent 1e163aa792
commit d27bda1c74
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
5 changed files with 80 additions and 60 deletions

View file

@ -10,20 +10,10 @@ interface Props {
isSecondary?: boolean; isSecondary?: boolean;
} }
const StyledButton = styled.button<Omit<Props, 'isLoading'>>` const ButtonStyle = styled.button<Omit<Props, 'isLoading'>>`
${tw`relative inline-block rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`}; ${tw`relative inline-block rounded p-2 uppercase tracking-wide text-sm transition-all duration-150 border`};
${props => props.isSecondary && css<Props>` ${props => ((!props.isSecondary && !props.color) || props.color === 'primary') && css<Props>`
${tw`border border-neutral-600 bg-transparent text-neutral-200`};
&:hover:not(:disabled) {
${tw`border-neutral-500 text-neutral-100`};
${props => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`};
${props => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`};
}
`};
${props => (!props.color || props.color === 'primary') && css<Props>`
${props => !props.isSecondary && tw`bg-primary-500 border-primary-600 border text-primary-50`}; ${props => !props.isSecondary && tw`bg-primary-500 border-primary-600 border text-primary-50`};
&:hover:not(:disabled) { &:hover:not(:disabled) {
@ -32,7 +22,7 @@ const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
`}; `};
${props => props.color === 'grey' && css` ${props => props.color === 'grey' && css`
${tw`border border-neutral-600 bg-neutral-500 text-neutral-50`}; ${tw`border-neutral-600 bg-neutral-500 text-neutral-50`};
&:hover:not(:disabled) { &:hover:not(:disabled) {
${tw`bg-neutral-600 border-neutral-700`}; ${tw`bg-neutral-600 border-neutral-700`};
@ -40,7 +30,7 @@ const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
`}; `};
${props => props.color === 'green' && css<Props>` ${props => props.color === 'green' && css<Props>`
${tw`border border-green-600 bg-green-500 text-green-50`}; ${tw`border-green-600 bg-green-500 text-green-50`};
&:hover:not(:disabled) { &:hover:not(:disabled) {
${tw`bg-green-600 border-green-700`}; ${tw`bg-green-600 border-green-700`};
@ -54,7 +44,7 @@ const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
`}; `};
${props => props.color === 'red' && css<Props>` ${props => props.color === 'red' && css<Props>`
${tw`border border-red-600 bg-red-500 text-red-50`}; ${tw`border-red-600 bg-red-500 text-red-50`};
&:hover:not(:disabled) { &:hover:not(:disabled) {
${tw`bg-red-600 border-red-700`}; ${tw`bg-red-600 border-red-700`};
@ -72,13 +62,23 @@ const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
${props => props.size === 'large' && tw`p-4 text-sm`}; ${props => props.size === 'large' && tw`p-4 text-sm`};
${props => props.size === 'xlarge' && tw`p-4 w-full`}; ${props => props.size === 'xlarge' && tw`p-4 w-full`};
${props => props.isSecondary && css<Props>`
${tw`border-neutral-600 bg-transparent text-neutral-200`};
&:hover:not(:disabled) {
${tw`border-neutral-500 text-neutral-100`};
${props => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`};
${props => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`};
}
`};
&:disabled { opacity: 0.55; cursor: default } &:disabled { opacity: 0.55; cursor: default }
`; `;
type ComponentProps = Omit<JSX.IntrinsicElements['button'], 'ref' | keyof Props> & Props; type ComponentProps = Omit<JSX.IntrinsicElements['button'], 'ref' | keyof Props> & Props;
const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => ( const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => (
<StyledButton {...props}> <ButtonStyle {...props}>
{isLoading && {isLoading &&
<div css={tw`flex absolute justify-center items-center w-full h-full left-0 top-0`}> <div css={tw`flex absolute justify-center items-center w-full h-full left-0 top-0`}>
<Spinner size={'small'}/> <Spinner size={'small'}/>
@ -87,7 +87,12 @@ const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) =>
<span css={isLoading ? tw`text-transparent` : undefined}> <span css={isLoading ? tw`text-transparent` : undefined}>
{children} {children}
</span> </span>
</StyledButton> </ButtonStyle>
); );
type LinkProps = Omit<JSX.IntrinsicElements['a'], 'ref' | keyof Props> & Props;
const LinkButton: React.FC<LinkProps> = props => <ButtonStyle as={'a'} {...props}/>;
export { LinkButton, ButtonStyle };
export default Button; export default Button;

View file

@ -5,6 +5,7 @@ import getWebsocketToken from '@/api/server/getWebsocketToken';
import ContentContainer from '@/components/elements/ContentContainer'; import ContentContainer from '@/components/elements/ContentContainer';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import Spinner from '@/components/elements/Spinner'; import Spinner from '@/components/elements/Spinner';
import tw from 'twin.macro';
export default () => { export default () => {
const server = ServerContext.useStoreState(state => state.server.data); const server = ServerContext.useStoreState(state => state.server.data);
@ -67,10 +68,10 @@ export default () => {
return ( return (
error ? error ?
<CSSTransition timeout={250} in={true} appear={true} classNames={'fade'}> <CSSTransition timeout={250} in={true} appear={true} classNames={'fade'}>
<div className={'bg-red-500 py-2'}> <div css={tw`bg-red-500 py-2`}>
<ContentContainer className={'flex items-center justify-center'}> <ContentContainer css={tw`flex items-center justify-center`}>
<Spinner size={'small'}/> <Spinner size={'small'}/>
<p className={'ml-2 text-sm text-red-100'}> <p css={tw`ml-2 text-sm text-red-100`}>
We&apos;re having some trouble connecting to your server, please wait... We&apos;re having some trouble connecting to your server, please wait...
</p> </p>
</ContentContainer> </ContentContainer>

View file

@ -1,12 +1,13 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ServerContext } from '@/state/server'; import { ServerContext } from '@/state/server';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import TitledGreyBox from '@/components/elements/TitledGreyBox'; import TitledGreyBox from '@/components/elements/TitledGreyBox';
import ConfirmationModal from '@/components/elements/ConfirmationModal'; import ConfirmationModal from '@/components/elements/ConfirmationModal';
import reinstallServer from '@/api/server/reinstallServer'; import reinstallServer from '@/api/server/reinstallServer';
import { Actions, useStoreActions } from 'easy-peasy'; import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
export default () => { export default () => {
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
@ -19,7 +20,11 @@ export default () => {
setIsSubmitting(true); setIsSubmitting(true);
reinstallServer(uuid) reinstallServer(uuid)
.then(() => { .then(() => {
addFlash({ key: 'settings', type: 'success', message: 'Your server has begun the reinstallation process.' }); addFlash({
key: 'settings',
type: 'success',
message: 'Your server has begun the reinstallation process.',
});
}) })
.catch(error => { .catch(error => {
console.error(error); console.error(error);
@ -30,10 +35,10 @@ export default () => {
setIsSubmitting(false); setIsSubmitting(false);
setModalVisible(false); setModalVisible(false);
}); });
} };
return ( return (
<TitledGreyBox title={'Reinstall Server'} className={'relative'}> <TitledGreyBox title={'Reinstall Server'} css={tw`relative`}>
<ConfirmationModal <ConfirmationModal
title={'Confirm server reinstallation'} title={'Confirm server reinstallation'}
buttonText={'Yes, reinstall server'} buttonText={'Yes, reinstall server'}
@ -42,21 +47,26 @@ export default () => {
visible={modalVisible} visible={modalVisible}
onDismissed={() => setModalVisible(false)} onDismissed={() => setModalVisible(false)}
> >
Your server will be stopped and some files may be deleted or modified during this process, are you sure you wish to continue? Your server will be stopped and some files may be deleted or modified during this process, are you sure
you wish to continue?
</ConfirmationModal> </ConfirmationModal>
<p className={'text-sm'}> <p css={tw`text-sm`}>
Reinstalling your server will stop it, and then re-run the installation script that initially Reinstalling your server will stop it, and then re-run the installation script that initially
set it up. <strong className={'font-bold'}>Some files may be deleted or modified during this process, set it up.&nbsp;
please back up your data before continuing.</strong> <strong css={tw`font-medium`}>
Some files may be deleted or modified during this process, please back up your data before
continuing.
</strong>
</p> </p>
<div className={'mt-6 text-right'}> <div css={tw`mt-6 text-right`}>
<button <Button
type={'button'} type={'button'}
className={'btn btn-sm btn-secondary btn-red'} color={'red'}
isSecondary
onClick={() => setModalVisible(true)} onClick={() => setModalVisible(true)}
> >
Reinstall Server Reinstall Server
</button> </Button>
</div> </div>
</TitledGreyBox> </TitledGreyBox>
); );

View file

@ -9,6 +9,8 @@ import { object, string } from 'yup';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import Button from '@/components/elements/Button';
import tw from 'twin.macro';
interface Values { interface Values {
name: string; name: string;
@ -18,19 +20,19 @@ const RenameServerBox = () => {
const { isSubmitting } = useFormikContext<Values>(); const { isSubmitting } = useFormikContext<Values>();
return ( return (
<TitledGreyBox title={'Change Server Name'} className={'relative'}> <TitledGreyBox title={'Change Server Name'} css={tw`relative`}>
<SpinnerOverlay visible={isSubmitting}/> <SpinnerOverlay visible={isSubmitting}/>
<Form className={'mb-0'}> <Form css={tw`mb-0`}>
<Field <Field
id={'name'} id={'name'}
name={'name'} name={'name'}
label={'Server Name'} label={'Server Name'}
type={'text'} type={'text'}
/> />
<div className={'mt-6 text-right'}> <div css={tw`mt-6 text-right`}>
<button type={'submit'} className={'btn btn-sm btn-primary'}> <Button type={'submit'}>
Save Save
</button> </Button>
</div> </div>
</Form> </Form>
</TitledGreyBox> </TitledGreyBox>

View file

@ -9,6 +9,10 @@ import FlashMessageRender from '@/components/FlashMessageRender';
import Can from '@/components/elements/Can'; import Can from '@/components/elements/Can';
import ReinstallServerBox from '@/components/server/settings/ReinstallServerBox'; import ReinstallServerBox from '@/components/server/settings/ReinstallServerBox';
import PageContentBlock from '@/components/elements/PageContentBlock'; import PageContentBlock from '@/components/elements/PageContentBlock';
import tw from 'twin.macro';
import Input from '@/components/elements/Input';
import Label from '@/components/elements/Label';
import Button, { LinkButton } from '@/components/elements/Button';
export default () => { export default () => {
const user = useStoreState<ApplicationStore, UserData>(state => state.user.data!); const user = useStoreState<ApplicationStore, UserData>(state => state.user.data!);
@ -16,52 +20,50 @@ export default () => {
return ( return (
<PageContentBlock> <PageContentBlock>
<FlashMessageRender byKey={'settings'} className={'mb-4'}/> <FlashMessageRender byKey={'settings'} css={tw`mb-4`}/>
<div className={'md:flex'}> <div css={tw`md:flex`}>
<Can action={'file.sftp'}> <Can action={'file.sftp'}>
<div className={'w-full md:flex-1 md:max-w-1/2 md:mr-10'}> <div css={tw`w-full md:flex-1 md:mr-10`}>
<TitledGreyBox title={'SFTP Details'}> <TitledGreyBox title={'SFTP Details'}>
<div> <div>
<label className={'input-dark-label'}>Server Address</label> <Label>Server Address</Label>
<input <Input
type={'text'} type={'text'}
className={'input-dark'}
value={`sftp://${server.sftpDetails.ip}:${server.sftpDetails.port}`} value={`sftp://${server.sftpDetails.ip}:${server.sftpDetails.port}`}
readOnly={true} readOnly
/> />
</div> </div>
<div className={'mt-6'}> <div css={tw`mt-6`}>
<label className={'input-dark-label'}>Username</label> <Label>Username</Label>
<input <Input
type={'text'} type={'text'}
className={'input-dark'}
value={`${user.username}.${server.id}`} value={`${user.username}.${server.id}`}
readOnly={true} readOnly
/> />
</div> </div>
<div className={'mt-6 flex items-center'}> <div css={tw`mt-6 flex items-center`}>
<div className={'flex-1'}> <div css={tw`flex-1`}>
<div className={'border-l-4 border-cyan-500 p-3'}> <div css={tw`border-l-4 border-cyan-500 p-3`}>
<p className={'text-xs text-neutral-200'}> <p css={tw`text-xs text-neutral-200`}>
Your SFTP password is the same as the password you use to access this panel. Your SFTP password is the same as the password you use to access this panel.
</p> </p>
</div> </div>
</div> </div>
<div className={'ml-4'}> <div css={tw`ml-4`}>
<a <LinkButton
isSecondary
href={`sftp://${user.username}.${server.id}@${server.sftpDetails.ip}:${server.sftpDetails.port}`} href={`sftp://${user.username}.${server.id}@${server.sftpDetails.ip}:${server.sftpDetails.port}`}
className={'btn btn-sm btn-secondary'}
> >
Launch SFTP Launch SFTP
</a> </LinkButton>
</div> </div>
</div> </div>
</TitledGreyBox> </TitledGreyBox>
</div> </div>
</Can> </Can>
<div className={'w-full mt-6 md:flex-1 md:max-w-1/2 md:mt-0'}> <div css={tw`w-full mt-6 md:flex-1 md:mt-0`}>
<Can action={'settings.rename'}> <Can action={'settings.rename'}>
<div className={'mb-6 md:mb-10'}> <div css={tw`mb-6 md:mb-10`}>
<RenameServerBox/> <RenameServerBox/>
</div> </div>
</Can> </Can>