Files
panel/resources/scripts/components/server/backups/CreateBackupButton.tsx

117 lines
4.7 KiB
TypeScript
Raw Normal View History

import React, { useEffect, useState } from 'react';
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
import { Field as FormikField, Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { boolean, object, string } from 'yup';
import Field from '@/components/elements/Field';
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
import useFlash from '@/plugins/useFlash';
import createServerBackup from '@/api/server/backups/createServerBackup';
import FlashMessageRender from '@/components/FlashMessageRender';
2020-07-04 16:26:07 -07:00
import Button from '@/components/elements/Button';
import tw from 'twin.macro';
2020-07-04 18:46:09 -07:00
import { Textarea } from '@/components/elements/Input';
import getServerBackups from '@/api/swr/getServerBackups';
2020-08-25 22:09:54 -07:00
import { ServerContext } from '@/state/server';
import FormikSwitch from '@/components/elements/FormikSwitch';
import Can from '@/components/elements/Can';
interface Values {
name: string;
ignored: string;
isLocked: boolean;
}
const ModalContent = ({ ...props }: RequiredModalProps) => {
const { isSubmitting } = useFormikContext<Values>();
return (
<Modal {...props} showSpinnerOverlay={isSubmitting}>
2020-07-04 16:26:07 -07:00
<Form>
<FlashMessageRender byKey={'backups:create'} css={tw`mb-4`} />
2020-07-04 16:26:07 -07:00
<h2 css={tw`text-2xl mb-6`}>Create server backup</h2>
<Field
name={'name'}
label={'Backup name'}
description={'If provided, the name that should be used to reference this backup.'}
/>
<div css={tw`mt-6`}>
<FormikFieldWrapper
name={'ignored'}
label={'Ignored Files & Directories'}
description={`
Enter the files or folders to ignore while generating this backup. Leave blank to use
2024-03-12 22:39:16 -04:00
the contents of the .panelignore file in the root of the server directory if present.
Wildcard matching of files and folders is supported in addition to negating a rule by
prefixing the path with an exclamation point.
`}
>
<FormikField as={Textarea} name={'ignored'} rows={6} />
</FormikFieldWrapper>
</div>
<Can action={'backup.delete'}>
<div css={tw`mt-6 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}>
<FormikSwitch
name={'isLocked'}
label={'Locked'}
description={'Prevents this backup from being deleted until explicitly unlocked.'}
/>
</div>
</Can>
<div css={tw`flex justify-end mt-6`}>
<Button type={'submit'} disabled={isSubmitting}>
Start backup
2020-07-04 16:26:07 -07:00
</Button>
</div>
</Form>
</Modal>
);
};
2020-04-06 22:25:54 -07:00
export default () => {
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
const { clearFlashes, clearAndAddHttpError } = useFlash();
const [visible, setVisible] = useState(false);
const { mutate } = getServerBackups();
2020-04-06 22:25:54 -07:00
useEffect(() => {
clearFlashes('backups:create');
}, [visible]);
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
2020-04-06 22:25:54 -07:00
clearFlashes('backups:create');
createServerBackup(uuid, values)
.then((backup) => {
mutate(
(data) => ({ ...data, items: data.items.concat(backup), backupCount: data.backupCount + 1 }),
false
);
setVisible(false);
})
.catch((error) => {
clearAndAddHttpError({ key: 'backups:create', error });
setSubmitting(false);
});
};
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>
)}
2020-10-03 16:25:39 -07:00
<Button css={tw`w-full sm:w-auto`} onClick={() => setVisible(true)}>
Create backup
2020-07-04 16:26:07 -07:00
</Button>
</>
);
};