Apply new eslint rules; default to prettier for styling

This commit is contained in:
DaneEveritt 2022-06-26 15:13:52 -04:00
parent f22cce8881
commit dc84af9937
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
218 changed files with 3876 additions and 3564 deletions

View file

@ -16,7 +16,7 @@ const BackupContainer = () => {
const { clearFlashes, clearAndAddHttpError } = useFlash();
const { data: backups, error, isValidating } = getServerBackups();
const backupLimit = ServerContext.useStoreState(state => state.server.data!.featureLimits.backups);
const backupLimit = ServerContext.useStoreState((state) => state.server.data!.featureLimits.backups);
useEffect(() => {
if (!error) {
@ -26,53 +26,49 @@ const BackupContainer = () => {
}
clearAndAddHttpError({ error, key: 'backups' });
}, [ error ]);
}, [error]);
if (!backups || (error && isValidating)) {
return <Spinner size={'large'} centered/>;
return <Spinner size={'large'} centered />;
}
return (
<ServerContentBlock title={'Backups'}>
<FlashMessageRender byKey={'backups'} css={tw`mb-4`}/>
<FlashMessageRender byKey={'backups'} css={tw`mb-4`} />
<Pagination data={backups} onPageSelect={setPage}>
{({ items }) => (
!items.length ?
{({ items }) =>
!items.length ? (
// Don't show any error messages if the server has no backups and the user cannot
// create additional ones for the server.
!backupLimit ?
null
:
!backupLimit ? null : (
<p css={tw`text-center text-sm text-neutral-300`}>
{page > 1 ?
'Looks like we\'ve run out of backups to show you, try going back a page.'
:
'It looks like there are no backups currently stored for this server.'
}
{page > 1
? "Looks like we've run out of backups to show you, try going back a page."
: 'It looks like there are no backups currently stored for this server.'}
</p>
:
items.map((backup, index) => <BackupRow
key={backup.uuid}
backup={backup}
css={index > 0 ? tw`mt-2` : undefined}
/>)
)}
)
) : (
items.map((backup, index) => (
<BackupRow key={backup.uuid} backup={backup} css={index > 0 ? tw`mt-2` : undefined} />
))
)
}
</Pagination>
{backupLimit === 0 &&
<p css={tw`text-center text-sm text-neutral-300`}>
Backups cannot be created for this server because the backup limit is set to 0.
</p>
}
{backupLimit === 0 && (
<p css={tw`text-center text-sm text-neutral-300`}>
Backups cannot be created for this server because the backup limit is set to 0.
</p>
)}
<Can action={'backup.create'}>
<div css={tw`mt-6 sm:flex items-center justify-end`}>
{(backupLimit > 0 && backups.backupCount > 0) &&
<p css={tw`text-sm text-neutral-300 mb-4 sm:mr-6 sm:mb-0`}>
{backups.backupCount} of {backupLimit} backups have been created for this server.
</p>
}
{backupLimit > 0 && backupLimit > backups.backupCount &&
<CreateBackupButton css={tw`w-full sm:w-auto`}/>
}
{backupLimit > 0 && backups.backupCount > 0 && (
<p css={tw`text-sm text-neutral-300 mb-4 sm:mr-6 sm:mb-0`}>
{backups.backupCount} of {backupLimit} backups have been created for this server.
</p>
)}
{backupLimit > 0 && backupLimit > backups.backupCount && (
<CreateBackupButton css={tw`w-full sm:w-auto`} />
)}
</div>
</Can>
</ServerContentBlock>
@ -80,10 +76,10 @@ const BackupContainer = () => {
};
export default () => {
const [ page, setPage ] = useState<number>(1);
const [page, setPage] = useState<number>(1);
return (
<ServerBackupContext.Provider value={{ page, setPage }}>
<BackupContainer/>
<BackupContainer />
</ServerBackupContext.Provider>
);
};

View file

@ -28,11 +28,11 @@ interface Props {
}
export default ({ backup }: Props) => {
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
const setServerFromState = ServerContext.useStoreActions(actions => actions.server.setServerFromState);
const [ modal, setModal ] = useState('');
const [ loading, setLoading ] = useState(false);
const [ truncate, setTruncate ] = useState(false);
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
const setServerFromState = ServerContext.useStoreActions((actions) => actions.server.setServerFromState);
const [modal, setModal] = useState('');
const [loading, setLoading] = useState(false);
const [truncate, setTruncate] = useState(false);
const { clearFlashes, clearAndAddHttpError } = useFlash();
const { mutate } = getServerBackups();
@ -40,11 +40,11 @@ export default ({ backup }: Props) => {
setLoading(true);
clearFlashes('backups');
getBackupDownloadUrl(uuid, backup.uuid)
.then(url => {
.then((url) => {
// @ts-ignore
window.location = url;
})
.catch(error => {
.catch((error) => {
console.error(error);
clearAndAddHttpError({ key: 'backups', error });
})
@ -55,12 +55,17 @@ export default ({ backup }: Props) => {
setLoading(true);
clearFlashes('backups');
deleteBackup(uuid, backup.uuid)
.then(() => mutate(data => ({
...data,
items: data.items.filter(b => b.uuid !== backup.uuid),
backupCount: data.backupCount - 1,
}), false))
.catch(error => {
.then(() =>
mutate(
(data) => ({
...data,
items: data.items.filter((b) => b.uuid !== backup.uuid),
backupCount: data.backupCount - 1,
}),
false
)
)
.catch((error) => {
console.error(error);
clearAndAddHttpError({ key: 'backups', error });
setLoading(false);
@ -72,11 +77,13 @@ export default ({ backup }: Props) => {
setLoading(true);
clearFlashes('backups');
restoreServerBackup(uuid, backup.uuid, truncate)
.then(() => setServerFromState(s => ({
...s,
status: 'restoring_backup',
})))
.catch(error => {
.then(() =>
setServerFromState((s) => ({
...s,
status: 'restoring_backup',
}))
)
.catch((error) => {
console.error(error);
clearAndAddHttpError({ key: 'backups', error });
})
@ -90,14 +97,23 @@ export default ({ backup }: Props) => {
}
http.post(`/api/client/servers/${uuid}/backups/${backup.uuid}/lock`)
.then(() => mutate(data => ({
...data,
items: data.items.map(b => b.uuid !== backup.uuid ? b : {
...b,
isLocked: !b.isLocked,
}),
}), false))
.catch(error => alert(httpErrorToHuman(error)))
.then(() =>
mutate(
(data) => ({
...data,
items: data.items.map((b) =>
b.uuid !== backup.uuid
? b
: {
...b,
isLocked: !b.isLocked,
}
),
}),
false
)
)
.catch((error) => alert(httpErrorToHuman(error)))
.then(() => setModal(''));
};
@ -123,17 +139,14 @@ export default ({ backup }: Props) => {
manager, or create additional backups until completed.
</p>
<p css={tw`mt-4 -mb-2 bg-gray-700 p-3 rounded`}>
<label
htmlFor={'restore_truncate'}
css={tw`text-base flex items-center cursor-pointer`}
>
<label htmlFor={'restore_truncate'} css={tw`text-base flex items-center cursor-pointer`}>
<Input
type={'checkbox'}
css={tw`text-red-500! w-5! h-5! mr-2`}
id={'restore_truncate'}
value={'true'}
checked={truncate}
onChange={() => setTruncate(s => !s)}
onChange={() => setTruncate((s) => !s)}
/>
Delete all files before restoring backup.
</label>
@ -148,28 +161,28 @@ export default ({ backup }: Props) => {
>
This is a permanent operation. The backup cannot be recovered once deleted.
</Dialog.Confirm>
<SpinnerOverlay visible={loading} fixed/>
{backup.isSuccessful ?
<SpinnerOverlay visible={loading} fixed />
{backup.isSuccessful ? (
<DropdownMenu
renderToggle={onClick => (
renderToggle={(onClick) => (
<button
onClick={onClick}
css={tw`text-gray-200 transition-colors duration-150 hover:text-gray-100 p-2`}
>
<FontAwesomeIcon icon={faEllipsisH}/>
<FontAwesomeIcon icon={faEllipsisH} />
</button>
)}
>
<div css={tw`text-sm`}>
<Can action={'backup.download'}>
<DropdownButtonRow onClick={doDownload}>
<FontAwesomeIcon fixedWidth icon={faCloudDownloadAlt} css={tw`text-xs`}/>
<FontAwesomeIcon fixedWidth icon={faCloudDownloadAlt} css={tw`text-xs`} />
<span css={tw`ml-2`}>Download</span>
</DropdownButtonRow>
</Can>
<Can action={'backup.restore'}>
<DropdownButtonRow onClick={() => setModal('restore')}>
<FontAwesomeIcon fixedWidth icon={faBoxOpen} css={tw`text-xs`}/>
<FontAwesomeIcon fixedWidth icon={faBoxOpen} css={tw`text-xs`} />
<span css={tw`ml-2`}>Restore</span>
</DropdownButtonRow>
</Can>
@ -183,24 +196,24 @@ export default ({ backup }: Props) => {
/>
{backup.isLocked ? 'Unlock' : 'Lock'}
</DropdownButtonRow>
{!backup.isLocked &&
<DropdownButtonRow danger onClick={() => setModal('delete')}>
<FontAwesomeIcon fixedWidth icon={faTrashAlt} css={tw`text-xs`}/>
<span css={tw`ml-2`}>Delete</span>
</DropdownButtonRow>
}
{!backup.isLocked && (
<DropdownButtonRow danger onClick={() => setModal('delete')}>
<FontAwesomeIcon fixedWidth icon={faTrashAlt} css={tw`text-xs`} />
<span css={tw`ml-2`}>Delete</span>
</DropdownButtonRow>
)}
</>
</Can>
</div>
</DropdownMenu>
:
) : (
<button
onClick={() => setModal('delete')}
css={tw`text-gray-200 transition-colors duration-150 hover:text-gray-100 p-2`}
>
<FontAwesomeIcon icon={faTrashAlt}/>
<FontAwesomeIcon icon={faTrashAlt} />
</button>
}
)}
</>
);
};

View file

@ -21,20 +21,27 @@ interface Props {
export default ({ backup, className }: Props) => {
const { mutate } = getServerBackups();
useWebsocketEvent(`${SocketEvent.BACKUP_COMPLETED}:${backup.uuid}` as SocketEvent, data => {
useWebsocketEvent(`${SocketEvent.BACKUP_COMPLETED}:${backup.uuid}` as SocketEvent, (data) => {
try {
const parsed = JSON.parse(data);
mutate(data => ({
...data,
items: data.items.map(b => b.uuid !== backup.uuid ? b : ({
...b,
isSuccessful: parsed.is_successful || true,
checksum: (parsed.checksum_type || '') + ':' + (parsed.checksum || ''),
bytes: parsed.file_size || 0,
completedAt: new Date(),
})),
}), false);
mutate(
(data) => ({
...data,
items: data.items.map((b) =>
b.uuid !== backup.uuid
? b
: {
...b,
isSuccessful: parsed.is_successful || true,
checksum: (parsed.checksum_type || '') + ':' + (parsed.checksum || ''),
bytes: parsed.file_size || 0,
completedAt: new Date(),
}
),
}),
false
);
} catch (e) {
console.warn(e);
}
@ -44,52 +51,50 @@ export default ({ backup, className }: Props) => {
<GreyRowBox css={tw`flex-wrap md:flex-nowrap items-center`} className={className}>
<div css={tw`flex items-center truncate w-full md:flex-1`}>
<div css={tw`mr-4`}>
{backup.completedAt !== null ?
backup.isLocked ?
<FontAwesomeIcon icon={faLock} css={tw`text-yellow-500`}/>
:
<FontAwesomeIcon icon={faArchive} css={tw`text-neutral-300`}/>
:
<Spinner size={'small'}/>
}
{backup.completedAt !== null ? (
backup.isLocked ? (
<FontAwesomeIcon icon={faLock} css={tw`text-yellow-500`} />
) : (
<FontAwesomeIcon icon={faArchive} css={tw`text-neutral-300`} />
)
) : (
<Spinner size={'small'} />
)}
</div>
<div css={tw`flex flex-col truncate`}>
<div css={tw`flex items-center text-sm mb-1`}>
{backup.completedAt !== null && !backup.isSuccessful &&
<span css={tw`bg-red-500 py-px px-2 rounded-full text-white text-xs uppercase border border-red-600 mr-2`}>
Failed
</span>
}
<p css={tw`break-words truncate`}>
{backup.name}
</p>
{(backup.completedAt !== null && backup.isSuccessful) &&
<span css={tw`ml-3 text-neutral-300 text-xs font-extralight hidden sm:inline`}>{bytesToString(backup.bytes)}</span>
}
{backup.completedAt !== null && !backup.isSuccessful && (
<span
css={tw`bg-red-500 py-px px-2 rounded-full text-white text-xs uppercase border border-red-600 mr-2`}
>
Failed
</span>
)}
<p css={tw`break-words truncate`}>{backup.name}</p>
{backup.completedAt !== null && backup.isSuccessful && (
<span css={tw`ml-3 text-neutral-300 text-xs font-extralight hidden sm:inline`}>
{bytesToString(backup.bytes)}
</span>
)}
</div>
<p css={tw`mt-1 md:mt-0 text-xs text-neutral-400 font-mono truncate`}>
{backup.checksum}
</p>
<p css={tw`mt-1 md:mt-0 text-xs text-neutral-400 font-mono truncate`}>{backup.checksum}</p>
</div>
</div>
<div css={tw`flex-1 md:flex-none md:w-48 mt-4 md:mt-0 md:ml-8 md:text-center`}>
<p
title={format(backup.createdAt, 'ddd, MMMM do, yyyy HH:mm:ss')}
css={tw`text-sm`}
>
<p title={format(backup.createdAt, 'ddd, MMMM do, yyyy HH:mm:ss')} css={tw`text-sm`}>
{formatDistanceToNow(backup.createdAt, { includeSeconds: true, addSuffix: true })}
</p>
<p css={tw`text-2xs text-neutral-500 uppercase mt-1`}>Created</p>
</div>
<Can action={[ 'backup.download', 'backup.restore', 'backup.delete' ]} matchAny>
<Can action={['backup.download', 'backup.restore', 'backup.delete']} matchAny>
<div css={tw`mt-4 md:mt-0 ml-6`} style={{ marginRight: '-0.5rem' }}>
{!backup.completedAt ?
{!backup.completedAt ? (
<div css={tw`p-2 invisible`}>
<FontAwesomeIcon icon={faEllipsisH}/>
<FontAwesomeIcon icon={faEllipsisH} />
</div>
:
<BackupContextMenu backup={backup}/>
}
) : (
<BackupContextMenu backup={backup} />
)}
</div>
</Can>
</GreyRowBox>

View file

@ -27,7 +27,7 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
return (
<Modal {...props} showSpinnerOverlay={isSubmitting}>
<Form>
<FlashMessageRender byKey={'backups:create'} css={tw`mb-4`}/>
<FlashMessageRender byKey={'backups:create'} css={tw`mb-4`} />
<h2 css={tw`text-2xl mb-6`}>Create server backup</h2>
<Field
name={'name'}
@ -45,7 +45,7 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
prefixing the path with an exclamation point.
`}
>
<FormikField as={Textarea} name={'ignored'} rows={6}/>
<FormikField as={Textarea} name={'ignored'} rows={6} />
</FormikFieldWrapper>
</div>
<Can action={'backup.delete'}>
@ -68,23 +68,26 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
};
export default () => {
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
const { clearFlashes, clearAndAddHttpError } = useFlash();
const [ visible, setVisible ] = useState(false);
const [visible, setVisible] = useState(false);
const { mutate } = getServerBackups();
useEffect(() => {
clearFlashes('backups:create');
}, [ visible ]);
}, [visible]);
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
clearFlashes('backups:create');
createServerBackup(uuid, values)
.then(backup => {
mutate(data => ({ ...data, items: data.items.concat(backup), backupCount: data.backupCount + 1 }), false);
.then((backup) => {
mutate(
(data) => ({ ...data, items: data.items.concat(backup), backupCount: data.backupCount + 1 }),
false
);
setVisible(false);
})
.catch(error => {
.catch((error) => {
clearAndAddHttpError({ key: 'backups:create', error });
setSubmitting(false);
});
@ -92,19 +95,19 @@ export default () => {
return (
<>
{visible &&
<Formik
onSubmit={submit}
initialValues={{ name: '', ignored: '', isLocked: false }}
validationSchema={object().shape({
name: string().max(191),
ignored: string(),
isLocked: boolean(),
})}
>
<ModalContent appear visible={visible} onDismissed={() => setVisible(false)}/>
</Formik>
}
{visible && (
<Formik
onSubmit={submit}
initialValues={{ name: '', ignored: '', isLocked: false }}
validationSchema={object().shape({
name: string().max(191),
ignored: string(),
isLocked: boolean(),
})}
>
<ModalContent appear visible={visible} onDismissed={() => setVisible(false)} />
</Formik>
)}
<Button css={tw`w-full sm:w-auto`} onClick={() => setVisible(true)}>
Create backup
</Button>