c28cba92e2
This allows entire components to be unmounted when the modal is hidden without affecting the fade in/out of the modal itself. This also makes it easier to programatically dismiss a modal without having to copy the visibility all over the place, and makes working with props much simpler in those modal components
123 lines
4.9 KiB
TypeScript
123 lines
4.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Schedule, Task } from '@/api/server/schedules/getServerSchedules';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
import { faCode, faFileArchive, faPencilAlt, faToggleOn, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
|
import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask';
|
|
import { httpErrorToHuman } from '@/api/http';
|
|
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
|
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
|
import Can from '@/components/elements/Can';
|
|
import useServer from '@/plugins/useServer';
|
|
import useFlash from '@/plugins/useFlash';
|
|
import { ServerContext } from '@/state/server';
|
|
import tw from 'twin.macro';
|
|
import ConfirmationModal from '@/components/elements/ConfirmationModal';
|
|
|
|
interface Props {
|
|
schedule: Schedule;
|
|
task: Task;
|
|
}
|
|
|
|
const getActionDetails = (action: string): [ string, any ] => {
|
|
switch (action) {
|
|
case 'command':
|
|
return [ 'Send Command', faCode ];
|
|
case 'power':
|
|
return [ 'Send Power Action', faToggleOn ];
|
|
case 'backup':
|
|
return [ 'Create Backup', faFileArchive ];
|
|
default:
|
|
return [ 'Unknown Action', faCode ];
|
|
}
|
|
};
|
|
|
|
export default ({ schedule, task }: Props) => {
|
|
const { uuid } = useServer();
|
|
const { clearFlashes, addError } = useFlash();
|
|
const [ visible, setVisible ] = useState(false);
|
|
const [ isLoading, setIsLoading ] = useState(false);
|
|
const [ isEditing, setIsEditing ] = useState(false);
|
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
|
|
|
const onConfirmDeletion = () => {
|
|
setIsLoading(true);
|
|
clearFlashes('schedules');
|
|
deleteScheduleTask(uuid, schedule.id, task.id)
|
|
.then(() => appendSchedule({
|
|
...schedule,
|
|
tasks: schedule.tasks.filter(t => t.id !== task.id),
|
|
}))
|
|
.catch(error => {
|
|
console.error(error);
|
|
setIsLoading(false);
|
|
addError({ message: httpErrorToHuman(error), key: 'schedules' });
|
|
});
|
|
};
|
|
|
|
const [ title, icon ] = getActionDetails(task.action);
|
|
|
|
return (
|
|
<div css={tw`flex items-center bg-neutral-700 border border-neutral-600 mb-2 px-6 py-4 rounded`}>
|
|
<SpinnerOverlay visible={isLoading} fixed size={'large'}/>
|
|
{isEditing && <TaskDetailsModal
|
|
schedule={schedule}
|
|
task={task}
|
|
onDismissed={() => setIsEditing(false)}
|
|
/>}
|
|
<ConfirmationModal
|
|
title={'Confirm task deletion'}
|
|
buttonText={'Delete Task'}
|
|
onConfirmed={onConfirmDeletion}
|
|
visible={visible}
|
|
onModalDismissed={() => setVisible(false)}
|
|
>
|
|
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 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 css={tw`mr-6`}>
|
|
<p css={tw`text-center mb-1`}>
|
|
{task.timeOffset}s
|
|
</p>
|
|
<p css={tw`text-neutral-300 uppercase text-2xs`}>
|
|
Delay Run By
|
|
</p>
|
|
</div>
|
|
}
|
|
<Can action={'schedule.update'}>
|
|
<button
|
|
type={'button'}
|
|
aria-label={'Edit scheduled task'}
|
|
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}/>
|
|
</button>
|
|
</Can>
|
|
<Can action={'schedule.update'}>
|
|
<button
|
|
type={'button'}
|
|
aria-label={'Delete scheduled task'}
|
|
css={tw`block text-sm p-2 text-neutral-500 hover:text-red-600 transition-colors duration-150`}
|
|
onClick={() => setVisible(true)}
|
|
>
|
|
<FontAwesomeIcon icon={faTrashAlt}/>
|
|
</button>
|
|
</Can>
|
|
</div>
|
|
);
|
|
};
|