179 lines
8.5 KiB
TypeScript
179 lines
8.5 KiB
TypeScript
|
import React, { useCallback, useEffect, useState } from 'react';
|
||
|
import { useHistory, useParams } from 'react-router-dom';
|
||
|
import getServerSchedule from '@/api/server/schedules/getServerSchedule';
|
||
|
import Spinner from '@/components/elements/Spinner';
|
||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||
|
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
|
||
|
import NewTaskButton from '@/components/server/schedules/NewTaskButton';
|
||
|
import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton';
|
||
|
import Can from '@/components/elements/Can';
|
||
|
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/index';
|
||
|
import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow';
|
||
|
import isEqual from 'react-fast-compare';
|
||
|
import { format } from 'date-fns';
|
||
|
import ScheduleCronRow from '@/components/server/schedules/ScheduleCronRow';
|
||
|
import RunScheduleButton from '@/components/server/schedules/RunScheduleButton';
|
||
|
|
||
|
import BeforeEdit from '@/blueprint/components/Server/Schedules/Edit/BeforeEdit';
|
||
|
import AfterEdit from '@/blueprint/components/Server/Schedules/Edit/AfterEdit';
|
||
|
|
||
|
interface Params {
|
||
|
id: string;
|
||
|
}
|
||
|
|
||
|
const CronBox = ({ title, value }: { title: string; value: string }) => (
|
||
|
<div css={tw`bg-neutral-700 rounded p-3`}>
|
||
|
<p css={tw`text-neutral-300 text-sm`}>{title}</p>
|
||
|
<p css={tw`text-xl font-medium text-neutral-100`}>{value}</p>
|
||
|
</div>
|
||
|
);
|
||
|
|
||
|
const ActivePill = ({ active }: { active: boolean }) => (
|
||
|
<span
|
||
|
css={[
|
||
|
tw`rounded-full px-2 py-px text-xs ml-4 uppercase`,
|
||
|
active ? tw`bg-green-600 text-green-100` : tw`bg-red-600 text-red-100`,
|
||
|
]}
|
||
|
>
|
||
|
{active ? 'Active' : 'Inactive'}
|
||
|
</span>
|
||
|
);
|
||
|
|
||
|
export default () => {
|
||
|
const history = useHistory();
|
||
|
const { id: scheduleId } = useParams<Params>();
|
||
|
|
||
|
const id = ServerContext.useStoreState((state) => state.server.data!.id);
|
||
|
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
|
||
|
|
||
|
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||
|
const [isLoading, setIsLoading] = useState(true);
|
||
|
const [showEditModal, setShowEditModal] = useState(false);
|
||
|
|
||
|
const schedule = ServerContext.useStoreState(
|
||
|
(st) => st.schedules.data.find((s) => s.id === Number(scheduleId)),
|
||
|
isEqual
|
||
|
);
|
||
|
const appendSchedule = ServerContext.useStoreActions((actions) => actions.schedules.appendSchedule);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (schedule?.id === Number(scheduleId)) {
|
||
|
setIsLoading(false);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
clearFlashes('schedules');
|
||
|
getServerSchedule(uuid, Number(scheduleId))
|
||
|
.then((schedule) => appendSchedule(schedule))
|
||
|
.catch((error) => {
|
||
|
console.error(error);
|
||
|
clearAndAddHttpError({ error, key: 'schedules' });
|
||
|
})
|
||
|
.then(() => setIsLoading(false));
|
||
|
}, [scheduleId]);
|
||
|
|
||
|
const toggleEditModal = useCallback(() => {
|
||
|
setShowEditModal((s) => !s);
|
||
|
}, []);
|
||
|
|
||
|
return (
|
||
|
<PageContentBlock title={'Schedules'}>
|
||
|
<FlashMessageRender byKey={'schedules'} css={tw`mb-4`} />
|
||
|
{!schedule || isLoading ? (
|
||
|
<Spinner size={'large'} centered />
|
||
|
) : (
|
||
|
<>
|
||
|
<BeforeEdit />
|
||
|
<ScheduleCronRow cron={schedule.cron} css={tw`sm:hidden bg-neutral-700 rounded mb-4 p-3`} />
|
||
|
<div css={tw`rounded shadow`}>
|
||
|
<div
|
||
|
css={tw`sm:flex items-center bg-neutral-900 p-3 sm:p-6 border-b-4 border-neutral-600 rounded-t`}
|
||
|
>
|
||
|
<div css={tw`flex-1`}>
|
||
|
<h3 css={tw`flex items-center text-neutral-100 text-2xl`}>
|
||
|
{schedule.name}
|
||
|
{schedule.isProcessing ? (
|
||
|
<span
|
||
|
css={tw`flex items-center rounded-full px-2 py-px text-xs ml-4 uppercase bg-neutral-600 text-white`}
|
||
|
>
|
||
|
<Spinner css={tw`w-3! h-3! mr-2`} />
|
||
|
Processing
|
||
|
</span>
|
||
|
) : (
|
||
|
<ActivePill active={schedule.isActive} />
|
||
|
)}
|
||
|
</h3>
|
||
|
<p css={tw`mt-1 text-sm text-neutral-200`}>
|
||
|
Last run at:
|
||
|
{schedule.lastRunAt ? (
|
||
|
format(schedule.lastRunAt, "MMM do 'at' h:mma")
|
||
|
) : (
|
||
|
<span css={tw`text-neutral-300`}>n/a</span>
|
||
|
)}
|
||
|
<span css={tw`ml-4 pl-4 border-l-4 border-neutral-600 py-px`}>
|
||
|
Next run at:
|
||
|
{schedule.nextRunAt ? (
|
||
|
format(schedule.nextRunAt, "MMM do 'at' h:mma")
|
||
|
) : (
|
||
|
<span css={tw`text-neutral-300`}>n/a</span>
|
||
|
)}
|
||
|
</span>
|
||
|
</p>
|
||
|
</div>
|
||
|
<div css={tw`flex sm:block mt-3 sm:mt-0`}>
|
||
|
<Can action={'schedule.update'}>
|
||
|
<Button.Text className={'flex-1 mr-4'} onClick={toggleEditModal}>
|
||
|
Edit
|
||
|
</Button.Text>
|
||
|
<NewTaskButton schedule={schedule} />
|
||
|
</Can>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div css={tw`hidden sm:grid grid-cols-5 md:grid-cols-5 gap-4 mb-4 mt-4`}>
|
||
|
<CronBox title={'Minute'} value={schedule.cron.minute} />
|
||
|
<CronBox title={'Hour'} value={schedule.cron.hour} />
|
||
|
<CronBox title={'Day (Month)'} value={schedule.cron.dayOfMonth} />
|
||
|
<CronBox title={'Month'} value={schedule.cron.month} />
|
||
|
<CronBox title={'Day (Week)'} value={schedule.cron.dayOfWeek} />
|
||
|
</div>
|
||
|
<div css={tw`bg-neutral-700 rounded-b`}>
|
||
|
{schedule.tasks.length > 0
|
||
|
? schedule.tasks
|
||
|
.sort((a, b) =>
|
||
|
a.sequenceId === b.sequenceId ? 0 : a.sequenceId > b.sequenceId ? 1 : -1
|
||
|
)
|
||
|
.map((task) => (
|
||
|
<ScheduleTaskRow
|
||
|
key={`${schedule.id}_${task.id}`}
|
||
|
task={task}
|
||
|
schedule={schedule}
|
||
|
/>
|
||
|
))
|
||
|
: null}
|
||
|
</div>
|
||
|
</div>
|
||
|
<EditScheduleModal visible={showEditModal} schedule={schedule} onModalDismissed={toggleEditModal} />
|
||
|
<div css={tw`mt-6 flex sm:justify-end`}>
|
||
|
<Can action={'schedule.delete'}>
|
||
|
<DeleteScheduleButton
|
||
|
scheduleId={schedule.id}
|
||
|
onDeleted={() => history.push(`/server/${id}/schedules`)}
|
||
|
/>
|
||
|
</Can>
|
||
|
{schedule.tasks.length > 0 && (
|
||
|
<Can action={'schedule.update'}>
|
||
|
<RunScheduleButton schedule={schedule} />
|
||
|
</Can>
|
||
|
)}
|
||
|
</div>
|
||
|
<AfterEdit />
|
||
|
</>
|
||
|
)}
|
||
|
</PageContentBlock>
|
||
|
);
|
||
|
};
|