2020-03-19 05:28:32 +00:00
import React , { useEffect } from 'react' ;
import Modal from '@/components/elements/Modal' ;
2020-04-10 17:46:00 +00:00
import { Schedule , Task } from '@/api/server/schedules/getServerSchedules' ;
2020-03-22 21:11:26 +00:00
import { Field as FormikField , Form , Formik , FormikHelpers , useFormikContext } from 'formik' ;
2020-03-19 05:28:32 +00:00
import { ServerContext } from '@/state/server' ;
import createOrUpdateScheduleTask from '@/api/server/schedules/createOrUpdateScheduleTask' ;
import { httpErrorToHuman } from '@/api/http' ;
import Field from '@/components/elements/Field' ;
import FlashMessageRender from '@/components/FlashMessageRender' ;
2020-03-19 05:36:19 +00:00
import { number , object , string } from 'yup' ;
2020-04-10 17:46:00 +00:00
import useFlash from '@/plugins/useFlash' ;
import useServer from '@/plugins/useServer' ;
2020-04-19 19:15:10 +00:00
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper' ;
2020-07-05 00:00:19 +00:00
import tw from 'twin.macro' ;
import Label from '@/components/elements/Label' ;
import { Textarea } from '@/components/elements/Input' ;
import Button from '@/components/elements/Button' ;
import Select from '@/components/elements/Select' ;
2020-03-19 05:28:32 +00:00
interface Props {
2020-04-10 17:46:00 +00:00
schedule : Schedule ;
2020-03-19 05:28:32 +00:00
// If a task is provided we can assume we're editing it. If not provided,
// we are creating a new one.
task? : Task ;
2020-04-10 17:46:00 +00:00
onDismissed : ( ) = > void ;
2020-03-19 05:28:32 +00:00
}
interface Values {
action : string ;
payload : string ;
timeOffset : string ;
}
2020-03-22 21:11:26 +00:00
const TaskDetailsForm = ( { isEditingTask } : { isEditingTask : boolean } ) = > {
2020-08-02 02:52:13 +00:00
const { values : { action } , initialValues , setFieldValue , setFieldTouched , isSubmitting } = useFormikContext < Values > ( ) ;
2020-03-22 21:11:26 +00:00
useEffect ( ( ) = > {
2020-08-02 02:52:13 +00:00
if ( action !== initialValues . action ) {
setFieldValue ( 'payload' , action === 'power' ? 'start' : '' ) ;
setFieldTouched ( 'payload' , false ) ;
} else {
setFieldValue ( 'payload' , initialValues . payload ) ;
setFieldTouched ( 'payload' , false ) ;
}
2020-04-10 17:46:00 +00:00
} , [ action ] ) ;
2020-03-22 21:11:26 +00:00
return (
2020-07-05 00:00:19 +00:00
< Form css = { tw ` m-0 ` } >
< h2 css = { tw ` text-2xl mb-6 ` } > { isEditingTask ? 'Edit Task' : 'Create Task' } < / h2 >
< div css = { tw ` flex ` } >
< div css = { tw ` mr-2 w-1/3 ` } >
< Label > Action < / Label >
2020-04-20 02:43:41 +00:00
< FormikFieldWrapper name = { 'action' } >
2020-07-05 00:00:19 +00:00
< FormikField as = { Select } name = { 'action' } >
2020-04-20 02:43:41 +00:00
< option value = { 'command' } > Send command < / option >
< option value = { 'power' } > Send power action < / option >
< option value = { 'backup' } > Create backup < / option >
< / FormikField >
< / FormikFieldWrapper >
2020-03-22 21:11:26 +00:00
< / div >
2020-07-05 00:00:19 +00:00
< div css = { tw ` flex-1 ` } >
2020-03-22 21:11:26 +00:00
{ action === 'command' ?
< Field
name = { 'payload' }
label = { 'Payload' }
description = { 'The command to send to the server when this task executes.' }
/ >
:
2020-04-20 02:43:41 +00:00
action === 'power' ?
< div >
2020-07-05 00:00:19 +00:00
< Label > Payload < / Label >
2020-04-20 02:43:41 +00:00
< FormikFieldWrapper name = { 'payload' } >
2020-07-05 00:00:19 +00:00
< FormikField as = { Select } name = { 'payload' } >
2020-04-20 02:43:41 +00:00
< option value = { 'start' } > Start the server < / option >
< option value = { 'restart' } > Restart the server < / option >
< option value = { 'stop' } > Stop the server < / option >
< option value = { 'kill' } > Terminate the server < / option >
< / FormikField >
< / FormikFieldWrapper >
< / div >
:
< div >
2020-07-05 00:00:19 +00:00
< Label > Ignored Files < / Label >
2020-06-19 04:00:04 +00:00
< FormikFieldWrapper
name = { 'payload' }
description = { 'Optional. Include the files and folders to be excluded in this backup. By default, the contents of your .pteroignore file will be used.' }
>
2020-07-05 00:00:19 +00:00
< FormikField as = { Textarea } name = { 'payload' } css = { tw ` h-32 ` } / >
2020-04-20 02:43:41 +00:00
< / FormikFieldWrapper >
< / div >
2020-03-22 21:11:26 +00:00
}
< / div >
< / div >
2020-07-05 00:00:19 +00:00
< div css = { tw ` mt-6 ` } >
2020-03-22 21:11:26 +00:00
< Field
name = { 'timeOffset' }
label = { 'Time offset (in seconds)' }
description = { 'The amount of time to wait after the previous task executes before running this one. If this is the first task on a schedule this will not be applied.' }
/ >
< / div >
2020-07-05 00:00:19 +00:00
< div css = { tw ` flex justify-end mt-6 ` } >
2020-08-02 02:49:38 +00:00
< Button type = { 'submit' } disabled = { isSubmitting } >
2020-03-22 21:11:26 +00:00
{ isEditingTask ? 'Save Changes' : 'Create Task' }
2020-07-05 00:00:19 +00:00
< / Button >
2020-03-22 21:11:26 +00:00
< / div >
< / Form >
) ;
} ;
2020-04-10 17:46:00 +00:00
export default ( { task , schedule , onDismissed } : Props ) = > {
const { uuid } = useServer ( ) ;
const { clearFlashes , addError } = useFlash ( ) ;
const appendSchedule = ServerContext . useStoreActions ( actions = > actions . schedules . appendSchedule ) ;
2020-03-19 05:28:32 +00:00
useEffect ( ( ) = > {
clearFlashes ( 'schedule:task' ) ;
} , [ ] ) ;
const submit = ( values : Values , { setSubmitting } : FormikHelpers < Values > ) = > {
clearFlashes ( 'schedule:task' ) ;
2020-04-10 17:46:00 +00:00
createOrUpdateScheduleTask ( uuid , schedule . id , task ? . id , values )
. then ( task = > {
let tasks = schedule . tasks . map ( t = > t . id === task . id ? task : t ) ;
if ( ! schedule . tasks . find ( t = > t . id === task . id ) ) {
tasks = [ . . . tasks , task ] ;
}
appendSchedule ( { . . . schedule , tasks } ) ;
onDismissed ( ) ;
} )
2020-03-19 05:28:32 +00:00
. catch ( error = > {
console . error ( error ) ;
setSubmitting ( false ) ;
addError ( { message : httpErrorToHuman ( error ) , key : 'schedule:task' } ) ;
} ) ;
} ;
return (
< Formik
onSubmit = { submit }
initialValues = { {
action : task?.action || 'command' ,
payload : task?.payload || '' ,
timeOffset : task?.timeOffset.toString ( ) || '0' ,
} }
2020-03-19 05:36:19 +00:00
validationSchema = { object ( ) . shape ( {
2020-04-20 02:43:41 +00:00
action : string ( ) . required ( ) . oneOf ( [ 'command' , 'power' , 'backup' ] ) ,
payload : string ( ) . when ( 'action' , {
is : v = > v !== 'backup' ,
then : string ( ) . required ( 'A task payload must be provided.' ) ,
otherwise : string ( ) ,
} ) ,
2020-03-19 05:36:19 +00:00
timeOffset : number ( ) . typeError ( 'The time offset must be a valid number between 0 and 900.' )
. required ( 'A time offset value must be provided.' )
. min ( 0 , 'The time offset must be at least 0 seconds.' )
. max ( 900 , 'The time offset must be less than 900 seconds.' ) ,
} ) }
2020-03-19 05:28:32 +00:00
>
2020-03-22 21:11:26 +00:00
{ ( { isSubmitting } ) = > (
2020-03-19 05:28:32 +00:00
< Modal
2020-07-05 00:00:19 +00:00
visible
appear
2020-03-19 05:28:32 +00:00
onDismissed = { ( ) = > onDismissed ( ) }
showSpinnerOverlay = { isSubmitting }
>
2020-07-05 00:00:19 +00:00
< FlashMessageRender byKey = { 'schedule:task' } css = { tw ` mb-4 ` } / >
2020-03-22 21:11:26 +00:00
< TaskDetailsForm isEditingTask = { typeof task !== 'undefined' } / >
2020-03-19 05:28:32 +00:00
< / Modal >
) }
< / Formik >
) ;
} ;