Update schedule page

This commit is contained in:
Dane Everitt 2020-07-04 17:00:19 -07:00
parent f3586056f4
commit a288374027
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
12 changed files with 180 additions and 168 deletions

View file

@ -18,7 +18,7 @@ const ConfirmationModal = ({ title, appear, children, visible, buttonText, onCon
showSpinnerOverlay={showSpinnerOverlay}
onDismissed={() => onDismissed()}
>
<h3 css={tw`mb-6`}>{title}</h3>
<h2 css={tw`text-2xl mb-6`}>{title}</h2>
<p css={tw`text-sm`}>{children}</p>
<div css={tw`flex items-center justify-end mt-8`}>
<Button isSecondary onClick={() => onDismissed()}>

View file

@ -0,0 +1,36 @@
import styled, { css } from 'styled-components/macro';
import tw from 'twin.macro';
interface Props {
hideDropdownArrow?: boolean;
}
const Select = styled.select<Props>`
${tw`shadow-none block p-3 pr-8 rounded border w-full text-sm transition-colors duration-150 ease-linear`};
&, &:hover:not(:disabled), &:focus {
${tw`outline-none`};
}
-webkit-appearance: none;
-moz-appearance: none;
background-size: 1rem;
background-repeat: no-repeat;
background-position-x: calc(100% - 0.75rem);
background-position-y: center;
&::-ms-expand {
display: none;
}
${props => !props.hideDropdownArrow && css`
${tw`bg-neutral-600 border-neutral-500 text-neutral-200`};
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='%23C3D1DF' d='M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z'/%3e%3c/svg%3e ");
&:hover:not(:disabled), &:focus {
${tw`border-neutral-400`};
}
`};
`;
export default Select;

View file

@ -2,6 +2,8 @@ import React, { useMemo } from 'react';
import styled from 'styled-components/macro';
import v4 from 'uuid/v4';
import tw from 'twin.macro';
import Label from '@/components/elements/Label';
import Input from '@/components/elements/Input';
const ToggleContainer = styled.div`
${tw`relative select-none w-12 leading-normal`};
@ -50,7 +52,7 @@ const Switch = ({ name, label, description, defaultChecked, onChange, children }
<div css={tw`flex items-center`}>
<ToggleContainer css={tw`flex-none`}>
{children
|| <input
|| <Input
id={uuid}
name={name}
type={'checkbox'}
@ -58,21 +60,20 @@ const Switch = ({ name, label, description, defaultChecked, onChange, children }
defaultChecked={defaultChecked}
/>
}
<label htmlFor={uuid}/>
<Label htmlFor={uuid}/>
</ToggleContainer>
{(label || description) &&
<div css={tw`ml-4 w-full`}>
{label &&
<label
<Label
css={[ tw`cursor-pointer`, !!description && tw`mb-0` ]}
className={'input-dark-label'}
htmlFor={uuid}
>
{label}
</label>
</Label>
}
{description &&
<p className={'input-help'}>
<p css={tw`text-neutral-400 text-sm mt-2`}>
{description}
</p>
}

View file

@ -1,26 +0,0 @@
import React from 'react';
import Modal, { RequiredModalProps } from '@/components/elements/Modal';
type Props = RequiredModalProps & {
onConfirmed: () => void;
}
export default ({ onConfirmed, ...props }: Props) => (
<Modal {...props}>
<h2>Confirm task deletion</h2>
<p className={'text-sm mt-4'}>
Are you sure you want to delete this task? This action cannot be undone.
</p>
<div className={'flex items-center justify-end mt-8'}>
<button className={'btn btn-secondary btn-sm'} onClick={() => props.onDismissed()}>
Cancel
</button>
<button className={'btn btn-red btn-sm ml-4'} onClick={() => {
props.onDismissed();
onConfirmed();
}}>
Delete Task
</button>
</div>
</Modal>
);

View file

@ -1,10 +1,12 @@
import React, { useState } from 'react';
import Modal from '@/components/elements/Modal';
import deleteSchedule from '@/api/server/schedules/deleteSchedule';
import { ServerContext } from '@/state/server';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
import { httpErrorToHuman } from '@/api/http';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import ConfirmationModal from '@/components/elements/ConfirmationModal';
interface Props {
scheduleId: number;
@ -36,34 +38,19 @@ export default ({ scheduleId, onDeleted }: Props) => {
return (
<>
<Modal
<ConfirmationModal
title={'Delete schedule?'}
buttonText={'Yes, delete schedule'}
onConfirmed={onDelete}
visible={visible}
onDismissed={() => setVisible(false)}
showSpinnerOverlay={isLoading}
>
<h3 className={'mb-6'}>Delete schedule</h3>
<p className={'text-sm'}>
Are you sure you want to delete this schedule? All tasks will be removed and any running processes
will be terminated.
</p>
<div className={'mt-6 flex justify-end'}>
<button
className={'btn btn-secondary btn-sm mr-4'}
onClick={() => setVisible(false)}
>
Cancel
</button>
<button
className={'btn btn-red btn-sm'}
onClick={() => onDelete()}
>
Yes, delete schedule
</button>
</div>
</Modal>
<button className={'btn btn-red btn-secondary btn-sm mr-4'} onClick={() => setVisible(true)}>
</ConfirmationModal>
<Button css={tw`mr-4`} color={'red'} isSecondary onClick={() => setVisible(true)}>
Delete
</button>
</Button>
</>
);
};

View file

@ -10,6 +10,8 @@ import { httpErrorToHuman } from '@/api/http';
import FlashMessageRender from '@/components/FlashMessageRender';
import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
type Props = {
schedule?: Schedule;
@ -29,43 +31,43 @@ const EditScheduleModal = ({ schedule, ...props }: Omit<Props, 'onScheduleUpdate
return (
<Modal {...props} showSpinnerOverlay={isSubmitting}>
<h3 className={'mb-6'}>{schedule ? 'Edit schedule' : 'Create new schedule'}</h3>
<FlashMessageRender byKey={'schedule:edit'} className={'mb-6'}/>
<h3 css={tw`mb-6`}>{schedule ? 'Edit schedule' : 'Create new schedule'}</h3>
<FlashMessageRender byKey={'schedule:edit'} css={tw`mb-6`}/>
<Form>
<Field
name={'name'}
label={'Schedule name'}
description={'A human readable identifer for this schedule.'}
/>
<div className={'flex mt-6'}>
<div className={'flex-1 mr-4'}>
<div css={tw`flex mt-6`}>
<div css={tw`flex-1 mr-4`}>
<Field name={'dayOfWeek'} label={'Day of week'}/>
</div>
<div className={'flex-1 mr-4'}>
<div css={tw`flex-1 mr-4`}>
<Field name={'dayOfMonth'} label={'Day of month'}/>
</div>
<div className={'flex-1 mr-4'}>
<div css={tw`flex-1 mr-4`}>
<Field name={'hour'} label={'Hour'}/>
</div>
<div className={'flex-1'}>
<div css={tw`flex-1`}>
<Field name={'minute'} label={'Minute'}/>
</div>
</div>
<p className={'input-help'}>
<p css={tw`text-neutral-400 text-xs mt-2`}>
The schedule system supports the use of Cronjob syntax when defining when tasks should begin
running. Use the fields above to specify when these tasks should begin running.
</p>
<div className={'mt-6 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded'}>
<div css={tw`mt-6 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}>
<FormikSwitch
name={'enabled'}
description={'If disabled, this schedule and it\'s associated tasks will not run.'}
label={'Enabled'}
/>
</div>
<div className={'mt-6 text-right'}>
<button className={'btn btn-sm btn-primary'} type={'submit'}>
<div css={tw`mt-6 text-right`}>
<Button type={'submit'}>
{schedule ? 'Save changes' : 'Create schedule'}
</button>
</Button>
</div>
</Form>
</Modal>

View file

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { Schedule } from '@/api/server/schedules/getServerSchedules';
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
import Button from '@/components/elements/Button';
interface Props {
schedule: Schedule;
@ -17,9 +18,9 @@ export default ({ schedule }: Props) => {
onDismissed={() => setVisible(false)}
/>
}
<button className={'btn btn-primary btn-sm'} onClick={() => setVisible(true)}>
<Button onClick={() => setVisible(true)}>
New Task
</button>
</Button>
</>
);
};

View file

@ -11,6 +11,9 @@ import Can from '@/components/elements/Can';
import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import PageContentBlock from '@/components/elements/PageContentBlock';
import tw from 'twin.macro';
import GreyRowBox from '@/components/elements/GreyRowBox';
import Button from '@/components/elements/Button';
export default ({ match, history }: RouteComponentProps) => {
const { uuid } = useServer();
@ -34,45 +37,38 @@ export default ({ match, history }: RouteComponentProps) => {
return (
<PageContentBlock>
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
<FlashMessageRender byKey={'schedules'} css={tw`mb-4`}/>
{(!schedules.length && loading) ?
<Spinner size={'large'} centered={true}/>
<Spinner size={'large'} centered/>
:
<>
{
schedules.length === 0 ?
<p className={'text-sm text-center text-neutral-400'}>
<p css={tw`text-sm text-center text-neutral-400`}>
There are no schedules configured for this server.
</p>
:
schedules.map(schedule => (
<a
<GreyRowBox
as={'a'}
key={schedule.id}
href={`${match.url}/${schedule.id}`}
className={'grey-row-box cursor-pointer mb-2'}
onClick={e => {
css={tw`cursor-pointer mb-2`}
onClick={(e: any) => {
e.preventDefault();
history.push(`${match.url}/${schedule.id}`, { schedule });
}}
>
<ScheduleRow schedule={schedule}/>
</a>
</GreyRowBox>
))
}
<Can action={'schedule.create'}>
<div className={'mt-8 flex justify-end'}>
{visible && <EditScheduleModal
appear={true}
visible={true}
onDismissed={() => setVisible(false)}
/>}
<button
type={'button'}
className={'btn btn-sm btn-primary'}
onClick={() => setVisible(true)}
>
<div css={tw`mt-8 flex justify-end`}>
{visible && <EditScheduleModal appear visible onDismissed={() => setVisible(false)}/>}
<Button type={'button'} onClick={() => setVisible(true)}>
Create schedule
</button>
</Button>
</div>
</Can>
</>

View file

@ -15,6 +15,9 @@ import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import { ServerContext } from '@/state/server';
import PageContentBlock from '@/components/elements/PageContentBlock';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
import GreyRowBox from '@/components/elements/GreyRowBox';
interface Params {
id: string;
@ -51,22 +54,22 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
return (
<PageContentBlock>
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
<FlashMessageRender byKey={'schedules'} css={tw`mb-4`}/>
{!schedule || isLoading ?
<Spinner size={'large'} centered={true}/>
<Spinner size={'large'} centered/>
:
<>
<div className={'grey-row-box'}>
<GreyRowBox>
<ScheduleRow schedule={schedule}/>
</div>
</GreyRowBox>
<EditScheduleModal
visible={showEditModal}
schedule={schedule}
onDismissed={() => setShowEditModal(false)}
/>
<div className={'flex items-center mt-8 mb-4'}>
<div className={'flex-1'}>
<h2>Configured Tasks</h2>
<div css={tw`flex items-center mt-8 mb-4`}>
<div css={tw`flex-1`}>
<h2 css={tw`text-2xl`}>Configured Tasks</h2>
</div>
</div>
{schedule.tasks.length > 0 ?
@ -79,17 +82,17 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
))
}
{schedule.tasks.length > 1 &&
<p className={'text-xs text-neutral-400'}>
<p css={tw`text-xs text-neutral-400`}>
Task delays are relative to the previous task in the listing.
</p>
}
</>
:
<p className={'text-sm text-neutral-400'}>
<p css={tw`text-sm text-neutral-400`}>
There are no tasks configured for this schedule.
</p>
}
<div className={'mt-8 flex justify-end'}>
<div css={tw`mt-8 flex justify-end`}>
<Can action={'schedule.delete'}>
<DeleteScheduleButton
scheduleId={schedule.id}
@ -97,9 +100,9 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
/>
</Can>
<Can action={'schedule.update'}>
<button className={'btn btn-primary btn-sm mr-4'} onClick={() => setShowEditModal(true)}>
<Button css={tw`mr-4`} onClick={() => setShowEditModal(true)}>
Edit
</button>
</Button>
<NewTaskButton schedule={schedule}/>
</Can>
</div>

View file

@ -4,47 +4,48 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons/faCalendarAlt';
import format from 'date-fns/format';
import classNames from 'classnames';
import tw from 'twin.macro';
export default ({ schedule }: { schedule: Schedule }) => (
<>
<div className={'icon'}>
<FontAwesomeIcon icon={faCalendarAlt} fixedWidth={true}/>
<div>
<FontAwesomeIcon icon={faCalendarAlt} fixedWidth/>
</div>
<div className={'flex-1 ml-4'}>
<div css={tw`flex-1 ml-4`}>
<p>{schedule.name}</p>
<p className={'text-xs text-neutral-400'}>
<p css={tw`text-xs text-neutral-400`}>
Last run
at: {schedule.lastRunAt ? format(schedule.lastRunAt, 'MMM Do [at] h:mma') : 'never'}
</p>
</div>
<div className={'flex items-center mx-8'}>
<div css={tw`flex items-center mx-8`}>
<div>
<p className={'font-medium text-center'}>{schedule.cron.minute}</p>
<p className={'text-2xs text-neutral-500 uppercase'}>Minute</p>
<p css={tw`font-medium text-center`}>{schedule.cron.minute}</p>
<p css={tw`text-2xs text-neutral-500 uppercase`}>Minute</p>
</div>
<div className={'ml-4'}>
<p className={'font-medium text-center'}>{schedule.cron.hour}</p>
<p className={'text-2xs text-neutral-500 uppercase'}>Hour</p>
<div css={tw`ml-4`}>
<p css={tw`font-medium text-center`}>{schedule.cron.hour}</p>
<p css={tw`text-2xs text-neutral-500 uppercase`}>Hour</p>
</div>
<div className={'ml-4'}>
<p className={'font-medium text-center'}>{schedule.cron.dayOfMonth}</p>
<p className={'text-2xs text-neutral-500 uppercase'}>Day (Month)</p>
<div css={tw`ml-4`}>
<p css={tw`font-medium text-center`}>{schedule.cron.dayOfMonth}</p>
<p css={tw`text-2xs text-neutral-500 uppercase`}>Day (Month)</p>
</div>
<div className={'ml-4'}>
<p className={'font-medium text-center'}>*</p>
<p className={'text-2xs text-neutral-500 uppercase'}>Month</p>
<div css={tw`ml-4`}>
<p css={tw`font-medium text-center`}>*</p>
<p css={tw`text-2xs text-neutral-500 uppercase`}>Month</p>
</div>
<div className={'ml-4'}>
<p className={'font-medium text-center'}>{schedule.cron.dayOfWeek}</p>
<p className={'text-2xs text-neutral-500 uppercase'}>Day (Week)</p>
<div css={tw`ml-4`}>
<p css={tw`font-medium text-center`}>{schedule.cron.dayOfWeek}</p>
<p css={tw`text-2xs text-neutral-500 uppercase`}>Day (Week)</p>
</div>
</div>
<div>
<p
className={classNames('py-1 px-3 rounded text-xs uppercase', {
'bg-green-600': schedule.isActive,
'bg-neutral-400': !schedule.isActive,
})}
css={[
tw`py-1 px-3 rounded text-xs uppercase text-white`,
schedule.isActive ? tw`bg-green-600` : tw`bg-neutral-400`,
]}
>
{schedule.isActive ? 'Active' : 'Inactive'}
</p>

View file

@ -4,7 +4,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt';
import { faCode } from '@fortawesome/free-solid-svg-icons/faCode';
import { faToggleOn } from '@fortawesome/free-solid-svg-icons/faToggleOn';
import ConfirmTaskDeletionModal from '@/components/server/schedules/ConfirmTaskDeletionModal';
import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask';
import { httpErrorToHuman } from '@/api/http';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
@ -15,6 +14,8 @@ import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import { ServerContext } from '@/state/server';
import { faFileArchive } from '@fortawesome/free-solid-svg-icons/faFileArchive';
import tw from 'twin.macro';
import ConfirmationModal from '@/components/elements/ConfirmationModal';
interface Props {
schedule: Schedule;
@ -60,38 +61,43 @@ export default ({ schedule, task }: Props) => {
const [ title, icon ] = getActionDetails(task.action);
return (
<div className={'flex items-center bg-neutral-700 border border-neutral-600 mb-2 px-6 py-4 rounded'}>
<div css={tw`flex items-center bg-neutral-700 border border-neutral-600 mb-2 px-6 py-4 rounded`}>
<SpinnerOverlay visible={isLoading} fixed={true} size={'large'}/>
{isEditing && <TaskDetailsModal
schedule={schedule}
task={task}
onDismissed={() => setIsEditing(false)}
/>}
<ConfirmTaskDeletionModal
<ConfirmationModal
title={'Confirm task deletion'}
buttonText={'Delete Task'}
onConfirmed={onConfirmDeletion}
visible={visible}
onDismissed={() => setVisible(false)}
onConfirmed={() => onConfirmDeletion()}
/>
<FontAwesomeIcon icon={icon} className={'text-lg text-white'}/>
<div className={'flex-1'}>
<p className={'ml-6 text-neutral-300 uppercase text-xs'}>
>
Are you sure you want to delete this task? This action cannot be undone.
</ConfirmationModal>
<FontAwesomeIcon icon={icon} css={tw`text-lg text-white`}/>
<div css={tw`flex-1`}>
<p css={tw`ml-6 text-neutral-300 uppercase text-xs`}>
{title}
</p>
{task.payload &&
<div className={'ml-6 mt-2'}>
{task.action === 'backup' && <p className={'text-xs uppercase text-neutral-400 mb-1'}>Ignoring files & folders:</p>}
<div className={'font-mono bg-neutral-800 rounded py-1 px-2 text-sm w-auto whitespace-pre inline-block'}>
<div css={tw`ml-6 mt-2`}>
{task.action === 'backup' &&
<p css={tw`text-xs uppercase text-neutral-400 mb-1`}>Ignoring files & folders:</p>}
<div css={tw`font-mono bg-neutral-800 rounded py-1 px-2 text-sm w-auto whitespace-pre inline-block`}>
{task.payload}
</div>
</div>
}
</div>
{task.sequenceId > 1 &&
<div className={'mr-6'}>
<p className={'text-center mb-1'}>
<div css={tw`mr-6`}>
<p css={tw`text-center mb-1`}>
{task.timeOffset}s
</p>
<p className={'text-neutral-300 uppercase text-2xs'}>
<p css={tw`text-neutral-300 uppercase text-2xs`}>
Delay Run By
</p>
</div>
@ -100,7 +106,7 @@ export default ({ schedule, task }: Props) => {
<button
type={'button'}
aria-label={'Edit scheduled task'}
className={'block text-sm p-2 text-neutral-500 hover:text-neutral-100 transition-colors duration-150 mr-4'}
css={tw`block text-sm p-2 text-neutral-500 hover:text-neutral-100 transition-colors duration-150 mr-4`}
onClick={() => setIsEditing(true)}
>
<FontAwesomeIcon icon={faPencilAlt}/>
@ -110,7 +116,7 @@ export default ({ schedule, task }: Props) => {
<button
type={'button'}
aria-label={'Delete scheduled task'}
className={'block text-sm p-2 text-neutral-500 hover:text-red-600 transition-colors duration-150'}
css={tw`block text-sm p-2 text-neutral-500 hover:text-red-600 transition-colors duration-150`}
onClick={() => setVisible(true)}
>
<FontAwesomeIcon icon={faTrashAlt}/>

View file

@ -11,6 +11,11 @@ import { number, object, string } from 'yup';
import useFlash from '@/plugins/useFlash';
import useServer from '@/plugins/useServer';
import FormikFieldWrapper from '@/components/elements/FormikFieldWrapper';
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';
interface Props {
schedule: Schedule;
@ -35,20 +40,20 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
}, [ action ]);
return (
<Form className={'m-0'}>
<h3 className={'mb-6'}>{isEditingTask ? 'Edit Task' : 'Create Task'}</h3>
<div className={'flex'}>
<div className={'mr-2 w-1/3'}>
<label className={'input-dark-label'}>Action</label>
<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>
<FormikFieldWrapper name={'action'}>
<FormikField as={'select'} name={'action'} className={'input-dark'}>
<FormikField as={Select} name={'action'}>
<option value={'command'}>Send command</option>
<option value={'power'}>Send power action</option>
<option value={'backup'}>Create backup</option>
</FormikField>
</FormikFieldWrapper>
</div>
<div className={'flex-1'}>
<div css={tw`flex-1`}>
{action === 'command' ?
<Field
name={'payload'}
@ -58,9 +63,9 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
:
action === 'power' ?
<div>
<label className={'input-dark-label'}>Payload</label>
<Label>Payload</Label>
<FormikFieldWrapper name={'payload'}>
<FormikField as={'select'} name={'payload'} className={'input-dark'}>
<FormikField as={Select} name={'payload'}>
<option value={'start'}>Start the server</option>
<option value={'restart'}>Restart the server</option>
<option value={'stop'}>Stop the server</option>
@ -70,28 +75,28 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
</div>
:
<div>
<label className={'input-dark-label'}>Ignored Files</label>
<Label>Ignored Files</Label>
<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.'}
>
<FormikField as={'textarea'} name={'payload'} className={'input-dark h-32'}/>
<FormikField as={Textarea} name={'payload'} css={tw`h-32`}/>
</FormikFieldWrapper>
</div>
}
</div>
</div>
<div className={'mt-6'}>
<div css={tw`mt-6`}>
<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>
<div className={'flex justify-end mt-6'}>
<button type={'submit'} className={'btn btn-primary btn-sm'}>
<div css={tw`flex justify-end mt-6`}>
<Button type={'submit'}>
{isEditingTask ? 'Save Changes' : 'Create Task'}
</button>
</Button>
</div>
</Form>
);
@ -148,12 +153,12 @@ export default ({ task, schedule, onDismissed }: Props) => {
>
{({ isSubmitting }) => (
<Modal
visible={true}
appear={true}
visible
appear
onDismissed={() => onDismissed()}
showSpinnerOverlay={isSubmitting}
>
<FlashMessageRender byKey={'schedule:task'} className={'mb-4'}/>
<FlashMessageRender byKey={'schedule:task'} css={tw`mb-4`}/>
<TaskDetailsForm isEditingTask={typeof task !== 'undefined'}/>
</Modal>
)}