This repository has been archived by the owner on Jun 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
925 additions
and
0 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
resources/scripts/components/server/schedules/DeleteScheduleButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React, { useState } from 'react'; | ||
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'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
interface Props { | ||
scheduleId: number; | ||
onDeleted: () => void; | ||
} | ||
|
||
export default ({ scheduleId, onDeleted }: Props) => { | ||
const [ visible, setVisible ] = useState(false); | ||
const [ isLoading, setIsLoading ] = useState(false); | ||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); | ||
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes); | ||
|
||
const onDelete = () => { | ||
setIsLoading(true); | ||
clearFlashes('schedules'); | ||
deleteSchedule(uuid, scheduleId) | ||
.then(() => { | ||
setIsLoading(false); | ||
onDeleted(); | ||
}) | ||
.catch(error => { | ||
console.error(error); | ||
|
||
addError({ key: 'schedules', message: httpErrorToHuman(error) }); | ||
setIsLoading(false); | ||
setVisible(false); | ||
}); | ||
}; | ||
|
||
return ( | ||
<> | ||
<ConfirmationModal | ||
visible={visible} | ||
title={lang.schedule_delete} | ||
buttonText={lang.schedule_yes_delete} | ||
onConfirmed={onDelete} | ||
showSpinnerOverlay={isLoading} | ||
onModalDismissed={() => setVisible(false)} | ||
> | ||
{lang.schedule_are_you_sure_delete} | ||
</ConfirmationModal> | ||
<Button css={tw`flex-1 sm:flex-none mr-4 border-transparent`} color={'red'} isSecondary onClick={() => setVisible(true)}> | ||
{lang.delete} | ||
</Button> | ||
</> | ||
); | ||
}; |
131 changes: 131 additions & 0 deletions
131
resources/scripts/components/server/schedules/EditScheduleModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import React, { useContext, useEffect } from 'react'; | ||
import { Schedule } from '@/api/server/schedules/getServerSchedules'; | ||
import Field from '@/components/elements/Field'; | ||
import { Form, Formik, FormikHelpers } from 'formik'; | ||
import FormikSwitch from '@/components/elements/FormikSwitch'; | ||
import createOrUpdateSchedule from '@/api/server/schedules/createOrUpdateSchedule'; | ||
import { ServerContext } from '@/state/server'; | ||
import { httpErrorToHuman } from '@/api/http'; | ||
import FlashMessageRender from '@/components/FlashMessageRender'; | ||
import useFlash from '@/plugins/useFlash'; | ||
import tw from 'twin.macro'; | ||
import Button from '@/components/elements/Button'; | ||
import ModalContext from '@/context/ModalContext'; | ||
import asModal from '@/hoc/asModal'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
interface Props { | ||
schedule?: Schedule; | ||
} | ||
|
||
interface Values { | ||
name: string; | ||
dayOfWeek: string; | ||
month: string; | ||
dayOfMonth: string; | ||
hour: string; | ||
minute: string; | ||
enabled: boolean; | ||
onlyWhenOnline: boolean; | ||
} | ||
|
||
const EditScheduleModal = ({ schedule }: Props) => { | ||
const { addError, clearFlashes } = useFlash(); | ||
const { dismiss } = useContext(ModalContext); | ||
|
||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); | ||
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule); | ||
|
||
useEffect(() => { | ||
return () => { | ||
clearFlashes('schedule:edit'); | ||
}; | ||
}, []); | ||
|
||
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => { | ||
clearFlashes('schedule:edit'); | ||
createOrUpdateSchedule(uuid, { | ||
id: schedule?.id, | ||
name: values.name, | ||
cron: { | ||
minute: values.minute, | ||
hour: values.hour, | ||
dayOfWeek: values.dayOfWeek, | ||
month: values.month, | ||
dayOfMonth: values.dayOfMonth, | ||
}, | ||
onlyWhenOnline: values.onlyWhenOnline, | ||
isActive: values.enabled, | ||
}) | ||
.then(schedule => { | ||
setSubmitting(false); | ||
appendSchedule(schedule); | ||
dismiss(); | ||
}) | ||
.catch(error => { | ||
console.error(error); | ||
|
||
setSubmitting(false); | ||
addError({ key: 'schedule:edit', message: httpErrorToHuman(error) }); | ||
}); | ||
}; | ||
|
||
return ( | ||
<Formik | ||
onSubmit={submit} | ||
initialValues={{ | ||
name: schedule?.name || '', | ||
minute: schedule?.cron.minute || '*/5', | ||
hour: schedule?.cron.hour || '*', | ||
dayOfMonth: schedule?.cron.dayOfMonth || '*', | ||
month: schedule?.cron.month || '*', | ||
dayOfWeek: schedule?.cron.dayOfWeek || '*', | ||
enabled: schedule?.isActive ?? true, | ||
onlyWhenOnline: schedule?.onlyWhenOnline ?? true, | ||
} as Values} | ||
> | ||
{({ isSubmitting }) => ( | ||
<Form> | ||
<h3 css={tw`text-2xl mb-6`}>{schedule ? lang.schedule_edit : lang.schedule_create_new}</h3> | ||
<FlashMessageRender byKey={'schedule:edit'} css={tw`mb-6`}/> | ||
<Field | ||
name={'name'} | ||
label={lang.schedule_name} | ||
description={lang.schedule_desc} | ||
/> | ||
<div css={tw`grid grid-cols-2 sm:grid-cols-5 gap-4 mt-6`}> | ||
<Field name={'minute'} label={lang.minute}/> | ||
<Field name={'hour'} label={lang.hour}/> | ||
<Field name={'dayOfMonth'} label={lang.day_of_month}/> | ||
<Field name={'month'} label={lang.month}/> | ||
<Field name={'dayOfWeek'} label={lang.day_of_week}/> | ||
</div> | ||
<p css={tw`text-neutral-400 text-xs mt-2`}> | ||
{lang.the_schedule_system_stuff_blabla_no_one_fucking_cares} | ||
</p> | ||
<div css={tw`mt-6 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}> | ||
<FormikSwitch | ||
name={'onlyWhenOnline'} | ||
description={lang.schedule_only_excec} | ||
label={lang.only_when_server_is_online} | ||
/> | ||
</div> | ||
<div css={tw`mt-6 bg-neutral-700 border border-neutral-800 shadow-inner p-4 rounded`}> | ||
<FormikSwitch | ||
name={'enabled'} | ||
description={lang.schedule_will_be_exec_if_enabled} | ||
label={lang.sceeeeeedule_enabled} | ||
/> | ||
</div> | ||
<div css={tw`mt-6 text-right`}> | ||
<Button css={tw`w-full sm:w-auto`} type={'submit'} disabled={isSubmitting}> | ||
{schedule ? lang.save_changes : lang.create_schedule} | ||
</Button> | ||
</div> | ||
</Form> | ||
)} | ||
</Formik> | ||
); | ||
}; | ||
|
||
export default asModal<Props>()(EditScheduleModal); |
23 changes: 23 additions & 0 deletions
23
resources/scripts/components/server/schedules/NewTaskButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
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'; | ||
import tw from 'twin.macro'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
interface Props { | ||
schedule: Schedule; | ||
} | ||
|
||
export default ({ schedule }: Props) => { | ||
const [ visible, setVisible ] = useState(false); | ||
|
||
return ( | ||
<> | ||
<TaskDetailsModal schedule={schedule} visible={visible} onModalDismissed={() => setVisible(false)}/> | ||
<Button onClick={() => setVisible(true)} css={tw`flex-1`}> | ||
{lang.new_task} | ||
</Button> | ||
</> | ||
); | ||
}; |
49 changes: 49 additions & 0 deletions
49
resources/scripts/components/server/schedules/RunScheduleButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React, { useCallback, useState } from 'react'; | ||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; | ||
import tw from 'twin.macro'; | ||
import Button from '@/components/elements/Button'; | ||
import triggerScheduleExecution from '@/api/server/schedules/triggerScheduleExecution'; | ||
import { ServerContext } from '@/state/server'; | ||
import useFlash from '@/plugins/useFlash'; | ||
import { Schedule } from '@/api/server/schedules/getServerSchedules'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
const RunScheduleButton = ({ schedule }: { schedule: Schedule }) => { | ||
const [ loading, setLoading ] = useState(false); | ||
const { clearFlashes, clearAndAddHttpError } = useFlash(); | ||
|
||
const id = ServerContext.useStoreState(state => state.server.data!.id); | ||
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule); | ||
|
||
const onTriggerExecute = useCallback(() => { | ||
clearFlashes('schedule'); | ||
setLoading(true); | ||
triggerScheduleExecution(id, schedule.id) | ||
.then(() => { | ||
setLoading(false); | ||
appendSchedule({ ...schedule, isProcessing: true }); | ||
}) | ||
.catch(error => { | ||
console.error(error); | ||
clearAndAddHttpError({ error, key: 'schedules' }); | ||
}) | ||
.then(() => setLoading(false)); | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<SpinnerOverlay visible={loading} size={'large'}/> | ||
<Button | ||
isSecondary | ||
color={'grey'} | ||
css={tw`flex-1 sm:flex-none border-transparent`} | ||
disabled={schedule.isProcessing} | ||
onClick={onTriggerExecute} | ||
> | ||
{lang.run_now} | ||
</Button> | ||
</> | ||
); | ||
}; | ||
|
||
export default RunScheduleButton; |
81 changes: 81 additions & 0 deletions
81
resources/scripts/components/server/schedules/ScheduleContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import getServerSchedules from '@/api/server/schedules/getServerSchedules'; | ||
import { ServerContext } from '@/state/server'; | ||
import Spinner from '@/components/elements/Spinner'; | ||
import { useHistory, useRouteMatch } from 'react-router-dom'; | ||
import FlashMessageRender from '@/components/FlashMessageRender'; | ||
import ScheduleRow from '@/components/server/schedules/ScheduleRow'; | ||
import { httpErrorToHuman } from '@/api/http'; | ||
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal'; | ||
import Can from '@/components/elements/Can'; | ||
import useFlash from '@/plugins/useFlash'; | ||
import tw from 'twin.macro'; | ||
import GreyRowBox from '@/components/elements/GreyRowBox'; | ||
import Button from '@/components/elements/Button'; | ||
import ServerContentBlock from '@/components/elements/ServerContentBlock'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
export default () => { | ||
const match = useRouteMatch(); | ||
const history = useHistory(); | ||
|
||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); | ||
const { clearFlashes, addError } = useFlash(); | ||
const [ loading, setLoading ] = useState(true); | ||
const [ visible, setVisible ] = useState(false); | ||
|
||
const schedules = ServerContext.useStoreState(state => state.schedules.data); | ||
const setSchedules = ServerContext.useStoreActions(actions => actions.schedules.setSchedules); | ||
|
||
useEffect(() => { | ||
clearFlashes('schedules'); | ||
getServerSchedules(uuid) | ||
.then(schedules => setSchedules(schedules)) | ||
.catch(error => { | ||
addError({ message: httpErrorToHuman(error), key: 'schedules' }); | ||
console.error(error); | ||
}) | ||
.then(() => setLoading(false)); | ||
}, []); | ||
|
||
return ( | ||
<ServerContentBlock title={'Schedules'}> | ||
<FlashMessageRender byKey={'schedules'} css={tw`mb-4`}/> | ||
{(!schedules.length && loading) ? | ||
<Spinner size={'large'} centered/> | ||
: | ||
<> | ||
{ | ||
schedules.length === 0 ? | ||
<p css={tw`text-sm text-center text-neutral-300`}> | ||
{lang.there_isnt_arent_schedules_for_server_smile} | ||
</p> | ||
: | ||
schedules.map(schedule => ( | ||
<GreyRowBox | ||
as={'a'} | ||
key={schedule.id} | ||
href={`${match.url}/${schedule.id}`} | ||
css={tw`cursor-pointer mb-2 flex-wrap`} | ||
onClick={(e: any) => { | ||
e.preventDefault(); | ||
history.push(`${match.url}/${schedule.id}`); | ||
}} | ||
> | ||
<ScheduleRow schedule={schedule}/> | ||
</GreyRowBox> | ||
)) | ||
} | ||
<Can action={'schedule.create'}> | ||
<div css={tw`mt-8 flex justify-end`}> | ||
<EditScheduleModal visible={visible} onModalDismissed={() => setVisible(false)}/> | ||
<Button type={'button'} onClick={() => setVisible(true)}> | ||
{lang.create_schedule} | ||
</Button> | ||
</div> | ||
</Can> | ||
</> | ||
} | ||
</ServerContentBlock> | ||
); | ||
}; |
36 changes: 36 additions & 0 deletions
36
resources/scripts/components/server/schedules/ScheduleCronRow.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import React from 'react'; | ||
import tw from 'twin.macro'; | ||
import { Schedule } from '@/api/server/schedules/getServerSchedules'; | ||
import lang from '../../../../../lang.json'; | ||
|
||
interface Props { | ||
cron: Schedule['cron']; | ||
className?: string; | ||
} | ||
|
||
const ScheduleCronRow = ({ cron, className }: Props) => ( | ||
<div css={tw`flex`} className={className}> | ||
<div css={tw`w-1/5 sm:w-auto text-center`}> | ||
<p css={tw`font-medium`}>{cron.minute}</p> | ||
<p css={tw`text-2xs text-neutral-500 uppercase`}>{lang.minute}</p> | ||
</div> | ||
<div css={tw`w-1/5 sm:w-auto text-center ml-4`}> | ||
<p css={tw`font-medium`}>{cron.hour}</p> | ||
<p css={tw`text-2xs text-neutral-500 uppercase`}>{lang.hour}</p> | ||
</div> | ||
<div css={tw`w-1/5 sm:w-auto text-center ml-4`}> | ||
<p css={tw`font-medium`}>{cron.dayOfMonth}</p> | ||
<p css={tw`text-2xs text-neutral-500 uppercase`}>{lang.day_month}</p> | ||
</div> | ||
<div css={tw`w-1/5 sm:w-auto text-center ml-4`}> | ||
<p css={tw`font-medium`}>{cron.month}</p> | ||
<p css={tw`text-2xs text-neutral-500 uppercase`}>{lang.month}</p> | ||
</div> | ||
<div css={tw`w-1/5 sm:w-auto text-center ml-4`}> | ||
<p css={tw`font-medium`}>{cron.dayOfWeek}</p> | ||
<p css={tw`text-2xs text-neutral-500 uppercase`}>{lang.day_week}</p> | ||
</div> | ||
</div> | ||
); | ||
|
||
export default ScheduleCronRow; |
Oops, something went wrong.