Clean up most of the schedules code to use server context
This commit is contained in:
parent
07d19ad326
commit
0ebf842757
|
@ -6,14 +6,13 @@ import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
import Switch from '@/components/elements/Switch';
|
import Switch from '@/components/elements/Switch';
|
||||||
import createOrUpdateSchedule from '@/api/server/schedules/createOrUpdateSchedule';
|
import createOrUpdateSchedule from '@/api/server/schedules/createOrUpdateSchedule';
|
||||||
import { ServerContext } from '@/state/server';
|
import { ServerContext } from '@/state/server';
|
||||||
import { Actions, useStoreActions } from 'easy-peasy';
|
|
||||||
import { ApplicationStore } from '@/state';
|
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
import useServer from '@/plugins/useServer';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
schedule?: Schedule;
|
schedule?: Schedule;
|
||||||
onScheduleUpdated: (schedule: Schedule) => void;
|
|
||||||
} & RequiredModalProps;
|
} & RequiredModalProps;
|
||||||
|
|
||||||
interface Values {
|
interface Values {
|
||||||
|
@ -73,11 +72,13 @@ const EditScheduleModal = ({ schedule, ...props }: Omit<Props, 'onScheduleUpdate
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ({ schedule, onScheduleUpdated, visible, ...props }: Props) => {
|
export default ({ schedule, visible, ...props }: Props) => {
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const { uuid } = useServer();
|
||||||
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
const { addError, clearFlashes } = useFlash();
|
||||||
const [ modalVisible, setModalVisible ] = useState(visible);
|
const [ modalVisible, setModalVisible ] = useState(visible);
|
||||||
|
|
||||||
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModalVisible(visible);
|
setModalVisible(visible);
|
||||||
clearFlashes('schedule:edit');
|
clearFlashes('schedule:edit');
|
||||||
|
@ -98,7 +99,7 @@ export default ({ schedule, onScheduleUpdated, visible, ...props }: Props) => {
|
||||||
})
|
})
|
||||||
.then(schedule => {
|
.then(schedule => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
onScheduleUpdated(schedule);
|
appendSchedule(schedule);
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Task } from '@/api/server/schedules/getServerSchedules';
|
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||||
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
scheduleId: number;
|
schedule: Schedule;
|
||||||
onTaskAdded: (task: Task) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ scheduleId, onTaskAdded }: Props) => {
|
export default ({ schedule }: Props) => {
|
||||||
const [ visible, setVisible ] = useState(false);
|
const [ visible, setVisible ] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{visible &&
|
{visible &&
|
||||||
<TaskDetailsModal
|
<TaskDetailsModal
|
||||||
scheduleId={scheduleId}
|
schedule={schedule}
|
||||||
onDismissed={task => {
|
onDismissed={() => setVisible(false)}
|
||||||
task && onTaskAdded(task);
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<button className={'btn btn-primary btn-sm'} onClick={() => setVisible(true)}>
|
<button className={'btn btn-primary btn-sm'} onClick={() => setVisible(true)}>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import getServerSchedules, { Schedule } from '@/api/server/schedules/getServerSchedules';
|
import getServerSchedules, { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||||
import { ServerContext } from '@/state/server';
|
import { ServerContext } from '@/state/server';
|
||||||
import Spinner from '@/components/elements/Spinner';
|
import Spinner from '@/components/elements/Spinner';
|
||||||
|
@ -10,27 +10,35 @@ import { Actions, useStoreActions } from 'easy-peasy';
|
||||||
import { ApplicationStore } from '@/state';
|
import { ApplicationStore } from '@/state';
|
||||||
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
|
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
|
||||||
import Can from '@/components/elements/Can';
|
import Can from '@/components/elements/Can';
|
||||||
|
import useServer from '@/plugins/useServer';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
|
||||||
|
|
||||||
export default ({ match, history }: RouteComponentProps) => {
|
export default ({ match, history }: RouteComponentProps) => {
|
||||||
const { uuid } = ServerContext.useStoreState(state => state.server.data!);
|
const { uuid } = useServer();
|
||||||
const [ schedules, setSchedules ] = useState<Schedule[] | null>(null);
|
const { clearFlashes, addError } = useFlash();
|
||||||
|
const [ loading, setLoading ] = useState(true);
|
||||||
const [ visible, setVisible ] = useState(false);
|
const [ visible, setVisible ] = useState(false);
|
||||||
const { clearFlashes, addError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
|
||||||
|
|
||||||
useMemo(() => {
|
const schedules = ServerContext.useStoreState(state => state.schedules.data);
|
||||||
|
const setSchedules = ServerContext.useStoreActions(actions => actions.schedules.setSchedules);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
clearFlashes('schedules');
|
clearFlashes('schedules');
|
||||||
getServerSchedules(uuid)
|
getServerSchedules(uuid)
|
||||||
.then(schedules => setSchedules(schedules))
|
.then(schedules => setSchedules(schedules))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
addError({ message: httpErrorToHuman(error), key: 'schedules' });
|
addError({ message: httpErrorToHuman(error), key: 'schedules' });
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
})
|
||||||
}, [ setSchedules ]);
|
.then(() => setLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'my-10 mb-6'}>
|
<div className={'my-10 mb-6'}>
|
||||||
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
|
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
|
||||||
{!schedules ?
|
<ListRefreshIndicator visible={loading}/>
|
||||||
|
{(!schedules.length && loading) ?
|
||||||
<Spinner size={'large'} centered={true}/>
|
<Spinner size={'large'} centered={true}/>
|
||||||
:
|
:
|
||||||
<>
|
<>
|
||||||
|
@ -59,7 +67,6 @@ export default ({ match, history }: RouteComponentProps) => {
|
||||||
{visible && <EditScheduleModal
|
{visible && <EditScheduleModal
|
||||||
appear={true}
|
appear={true}
|
||||||
visible={true}
|
visible={true}
|
||||||
onScheduleUpdated={schedule => setSchedules(s => [ ...(s || []), schedule ])}
|
|
||||||
onDismissed={() => setVisible(false)}
|
onDismissed={() => setVisible(false)}
|
||||||
/>}
|
/>}
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -2,11 +2,8 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { RouteComponentProps } from 'react-router-dom';
|
import { RouteComponentProps } from 'react-router-dom';
|
||||||
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||||
import getServerSchedule from '@/api/server/schedules/getServerSchedule';
|
import getServerSchedule from '@/api/server/schedules/getServerSchedule';
|
||||||
import { ServerContext } from '@/state/server';
|
|
||||||
import Spinner from '@/components/elements/Spinner';
|
import Spinner from '@/components/elements/Spinner';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
import { Actions, useStoreActions } from 'easy-peasy';
|
|
||||||
import { ApplicationStore } from '@/state';
|
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
|
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
|
||||||
import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow';
|
import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow';
|
||||||
|
@ -14,6 +11,9 @@ import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
|
||||||
import NewTaskButton from '@/components/server/schedules/NewTaskButton';
|
import NewTaskButton from '@/components/server/schedules/NewTaskButton';
|
||||||
import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton';
|
import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton';
|
||||||
import Can from '@/components/elements/Can';
|
import Can from '@/components/elements/Can';
|
||||||
|
import useServer from '@/plugins/useServer';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import { ServerContext } from '@/state/server';
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -24,11 +24,13 @@ interface State {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ match, history, location: { state } }: RouteComponentProps<Params, {}, State>) => {
|
export default ({ match, history, location: { state } }: RouteComponentProps<Params, {}, State>) => {
|
||||||
const { id, uuid } = ServerContext.useStoreState(state => state.server.data!);
|
const { id, uuid } = useServer();
|
||||||
|
const { clearFlashes, addError } = useFlash();
|
||||||
const [ isLoading, setIsLoading ] = useState(true);
|
const [ isLoading, setIsLoading ] = useState(true);
|
||||||
const [ showEditModal, setShowEditModal ] = useState(false);
|
const [ showEditModal, setShowEditModal ] = useState(false);
|
||||||
const [ schedule, setSchedule ] = useState<Schedule | undefined>(state?.schedule);
|
|
||||||
const { clearFlashes, addError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
const schedule = ServerContext.useStoreState(st => st.schedules.data.find(s => s.id === state.schedule?.id), [ match ]);
|
||||||
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (schedule?.id === Number(match.params.id)) {
|
if (schedule?.id === Number(match.params.id)) {
|
||||||
|
@ -38,13 +40,13 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
|
||||||
|
|
||||||
clearFlashes('schedules');
|
clearFlashes('schedules');
|
||||||
getServerSchedule(uuid, Number(match.params.id))
|
getServerSchedule(uuid, Number(match.params.id))
|
||||||
.then(schedule => setSchedule(schedule))
|
.then(schedule => appendSchedule(schedule))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
addError({ message: httpErrorToHuman(error), key: 'schedules' });
|
addError({ message: httpErrorToHuman(error), key: 'schedules' });
|
||||||
})
|
})
|
||||||
.then(() => setIsLoading(false));
|
.then(() => setIsLoading(false));
|
||||||
}, [ schedule, match ]);
|
}, [ match ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'my-10 mb-6'}>
|
<div className={'my-10 mb-6'}>
|
||||||
|
@ -59,7 +61,6 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
|
||||||
<EditScheduleModal
|
<EditScheduleModal
|
||||||
visible={showEditModal}
|
visible={showEditModal}
|
||||||
schedule={schedule}
|
schedule={schedule}
|
||||||
onScheduleUpdated={schedule => setSchedule(schedule)}
|
|
||||||
onDismissed={() => setShowEditModal(false)}
|
onDismissed={() => setShowEditModal(false)}
|
||||||
/>
|
/>
|
||||||
<div className={'flex items-center mt-8 mb-4'}>
|
<div className={'flex items-center mt-8 mb-4'}>
|
||||||
|
@ -67,23 +68,13 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
|
||||||
<h2>Configured Tasks</h2>
|
<h2>Configured Tasks</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{schedule?.tasks.length > 0 ?
|
{schedule.tasks.length > 0 ?
|
||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
schedule.tasks
|
schedule.tasks
|
||||||
.sort((a, b) => a.sequenceId - b.sequenceId)
|
.sort((a, b) => a.sequenceId - b.sequenceId)
|
||||||
.map(task => (
|
.map(task => (
|
||||||
<ScheduleTaskRow
|
<ScheduleTaskRow key={task.id} task={task} schedule={schedule}/>
|
||||||
key={task.id}
|
|
||||||
task={task}
|
|
||||||
schedule={schedule.id}
|
|
||||||
onTaskUpdated={task => setSchedule(s => ({
|
|
||||||
...s!, tasks: s!.tasks.map(t => t.id === task.id ? task : t),
|
|
||||||
}))}
|
|
||||||
onTaskRemoved={() => setSchedule(s => ({
|
|
||||||
...s!, tasks: s!.tasks.filter(t => t.id !== task.id),
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
{schedule.tasks.length > 1 &&
|
{schedule.tasks.length > 1 &&
|
||||||
|
@ -108,12 +99,7 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
|
||||||
<button className={'btn btn-primary btn-sm mr-4'} onClick={() => setShowEditModal(true)}>
|
<button className={'btn btn-primary btn-sm mr-4'} onClick={() => setShowEditModal(true)}>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
<NewTaskButton
|
<NewTaskButton schedule={schedule}/>
|
||||||
scheduleId={schedule.id}
|
|
||||||
onTaskAdded={task => setSchedule(s => ({
|
|
||||||
...s!, tasks: [ ...s!.tasks, task ],
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
</Can>
|
</Can>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,39 +1,41 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Task } from '@/api/server/schedules/getServerSchedules';
|
import { Schedule, Task } from '@/api/server/schedules/getServerSchedules';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt';
|
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons/faTrashAlt';
|
||||||
import { faCode } from '@fortawesome/free-solid-svg-icons/faCode';
|
import { faCode } from '@fortawesome/free-solid-svg-icons/faCode';
|
||||||
import { faToggleOn } from '@fortawesome/free-solid-svg-icons/faToggleOn';
|
import { faToggleOn } from '@fortawesome/free-solid-svg-icons/faToggleOn';
|
||||||
import ConfirmTaskDeletionModal from '@/components/server/schedules/ConfirmTaskDeletionModal';
|
import ConfirmTaskDeletionModal from '@/components/server/schedules/ConfirmTaskDeletionModal';
|
||||||
import { ServerContext } from '@/state/server';
|
|
||||||
import { Actions, useStoreActions } from 'easy-peasy';
|
|
||||||
import { ApplicationStore } from '@/state';
|
|
||||||
import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask';
|
import deleteScheduleTask from '@/api/server/schedules/deleteScheduleTask';
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||||
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
import TaskDetailsModal from '@/components/server/schedules/TaskDetailsModal';
|
||||||
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt';
|
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons/faPencilAlt';
|
||||||
import Can from '@/components/elements/Can';
|
import Can from '@/components/elements/Can';
|
||||||
|
import useServer from '@/plugins/useServer';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import { ServerContext } from '@/state/server';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
schedule: number;
|
schedule: Schedule;
|
||||||
task: Task;
|
task: Task;
|
||||||
onTaskUpdated: (task: Task) => void;
|
|
||||||
onTaskRemoved: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ schedule, task, onTaskUpdated, onTaskRemoved }: Props) => {
|
export default ({ schedule, task }: Props) => {
|
||||||
|
const { uuid } = useServer();
|
||||||
|
const { clearFlashes, addError } = useFlash();
|
||||||
const [ visible, setVisible ] = useState(false);
|
const [ visible, setVisible ] = useState(false);
|
||||||
const [ isLoading, setIsLoading ] = useState(false);
|
const [ isLoading, setIsLoading ] = useState(false);
|
||||||
const [ isEditing, setIsEditing ] = useState(false);
|
const [ isEditing, setIsEditing ] = useState(false);
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
||||||
const { clearFlashes, addError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
|
||||||
|
|
||||||
const onConfirmDeletion = () => {
|
const onConfirmDeletion = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
clearFlashes('schedules');
|
clearFlashes('schedules');
|
||||||
deleteScheduleTask(uuid, schedule, task.id)
|
deleteScheduleTask(uuid, schedule.id, task.id)
|
||||||
.then(() => onTaskRemoved())
|
.then(() => appendSchedule({
|
||||||
|
...schedule,
|
||||||
|
tasks: schedule.tasks.filter(t => t.id !== task.id),
|
||||||
|
}))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
@ -45,12 +47,9 @@ export default ({ schedule, task, onTaskUpdated, onTaskRemoved }: Props) => {
|
||||||
<div className={'flex items-center bg-neutral-700 border border-neutral-600 mb-2 px-6 py-4 rounded'}>
|
<div className={'flex items-center bg-neutral-700 border border-neutral-600 mb-2 px-6 py-4 rounded'}>
|
||||||
<SpinnerOverlay visible={isLoading} fixed={true} size={'large'}/>
|
<SpinnerOverlay visible={isLoading} fixed={true} size={'large'}/>
|
||||||
{isEditing && <TaskDetailsModal
|
{isEditing && <TaskDetailsModal
|
||||||
scheduleId={schedule}
|
schedule={schedule}
|
||||||
task={task}
|
task={task}
|
||||||
onDismissed={task => {
|
onDismissed={() => setIsEditing(false)}
|
||||||
task && onTaskUpdated(task);
|
|
||||||
setIsEditing(false);
|
|
||||||
}}
|
|
||||||
/>}
|
/>}
|
||||||
<ConfirmTaskDeletionModal
|
<ConfirmTaskDeletionModal
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import Modal from '@/components/elements/Modal';
|
import Modal from '@/components/elements/Modal';
|
||||||
import { Task } from '@/api/server/schedules/getServerSchedules';
|
import { Schedule, Task } from '@/api/server/schedules/getServerSchedules';
|
||||||
import { Field as FormikField, Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
import { Field as FormikField, Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
import { ServerContext } from '@/state/server';
|
import { ServerContext } from '@/state/server';
|
||||||
import { Actions, useStoreActions } from 'easy-peasy';
|
|
||||||
import { ApplicationStore } from '@/state';
|
|
||||||
import createOrUpdateScheduleTask from '@/api/server/schedules/createOrUpdateScheduleTask';
|
import createOrUpdateScheduleTask from '@/api/server/schedules/createOrUpdateScheduleTask';
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
import Field from '@/components/elements/Field';
|
import Field from '@/components/elements/Field';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
import { number, object, string } from 'yup';
|
import { number, object, string } from 'yup';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import useServer from '@/plugins/useServer';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
scheduleId: number;
|
schedule: Schedule;
|
||||||
// If a task is provided we can assume we're editing it. If not provided,
|
// If a task is provided we can assume we're editing it. If not provided,
|
||||||
// we are creating a new one.
|
// we are creating a new one.
|
||||||
task?: Task;
|
task?: Task;
|
||||||
onDismissed: (task: Task | undefined | void) => void;
|
onDismissed: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Values {
|
interface Values {
|
||||||
|
@ -29,8 +29,10 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
|
||||||
const { values: { action }, setFieldValue, setFieldTouched } = useFormikContext<Values>();
|
const { values: { action }, setFieldValue, setFieldTouched } = useFormikContext<Values>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
setFieldValue('payload', '');
|
setFieldValue('payload', '');
|
||||||
setFieldTouched('payload', false);
|
setFieldTouched('payload', false);
|
||||||
|
};
|
||||||
}, [ action ]);
|
}, [ action ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -80,9 +82,10 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ({ task, scheduleId, onDismissed }: Props) => {
|
export default ({ task, schedule, onDismissed }: Props) => {
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const { uuid } = useServer();
|
||||||
const { clearFlashes, addError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
const { clearFlashes, addError } = useFlash();
|
||||||
|
const appendSchedule = ServerContext.useStoreActions(actions => actions.schedules.appendSchedule);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearFlashes('schedule:task');
|
clearFlashes('schedule:task');
|
||||||
|
@ -90,8 +93,16 @@ export default ({ task, scheduleId, onDismissed }: Props) => {
|
||||||
|
|
||||||
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
clearFlashes('schedule:task');
|
clearFlashes('schedule:task');
|
||||||
createOrUpdateScheduleTask(uuid, scheduleId, task?.id, values)
|
createOrUpdateScheduleTask(uuid, schedule.id, task?.id, values)
|
||||||
.then(task => onDismissed(task))
|
.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();
|
||||||
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
import getServerSubusers from '@/api/server/users/getServerSubusers';
|
import getServerSubusers from '@/api/server/users/getServerSubusers';
|
||||||
import { httpErrorToHuman } from '@/api/http';
|
import { httpErrorToHuman } from '@/api/http';
|
||||||
import Can from '@/components/elements/Can';
|
import Can from '@/components/elements/Can';
|
||||||
|
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [ loading, setLoading ] = useState(true);
|
const [ loading, setLoading ] = useState(true);
|
||||||
|
@ -21,10 +22,6 @@ export default () => {
|
||||||
const getPermissions = useStoreActions((actions: Actions<ApplicationStore>) => actions.permissions.getPermissions);
|
const getPermissions = useStoreActions((actions: Actions<ApplicationStore>) => actions.permissions.getPermissions);
|
||||||
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getPermissions().catch(error => console.error(error));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearFlashes('users');
|
clearFlashes('users');
|
||||||
getServerSubusers(uuid)
|
getServerSubusers(uuid)
|
||||||
|
@ -38,12 +35,20 @@ export default () => {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (loading || !Object.keys(permissions).length) {
|
useEffect(() => {
|
||||||
|
getPermissions().catch(error => {
|
||||||
|
addError({ key: 'users', message: httpErrorToHuman(error) });
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!subusers.length && (loading || !Object.keys(permissions).length)) {
|
||||||
return <Spinner size={'large'} centered={true}/>;
|
return <Spinner size={'large'} centered={true}/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'mt-10 mb-6'}>
|
<div className={'mt-10 mb-6'}>
|
||||||
|
<ListRefreshIndicator visible={loading}/>
|
||||||
<FlashMessageRender byKey={'users'} className={'mb-4'}/>
|
<FlashMessageRender byKey={'users'} className={'mb-4'}/>
|
||||||
{!subusers.length ?
|
{!subusers.length ?
|
||||||
<p className={'text-center text-sm text-neutral-400'}>
|
<p className={'text-center text-sm text-neutral-400'}>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import files, { ServerFileStore } from '@/state/server/files';
|
||||||
import subusers, { ServerSubuserStore } from '@/state/server/subusers';
|
import subusers, { ServerSubuserStore } from '@/state/server/subusers';
|
||||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||||
import backups, { ServerBackupStore } from '@/state/server/backups';
|
import backups, { ServerBackupStore } from '@/state/server/backups';
|
||||||
|
import schedules, { ServerScheduleStore } from '@/state/server/schedules';
|
||||||
|
|
||||||
export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
|
export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
|
||||||
|
|
||||||
|
@ -74,6 +75,7 @@ export interface ServerStore {
|
||||||
subusers: ServerSubuserStore;
|
subusers: ServerSubuserStore;
|
||||||
databases: ServerDatabaseStore;
|
databases: ServerDatabaseStore;
|
||||||
files: ServerFileStore;
|
files: ServerFileStore;
|
||||||
|
schedules: ServerScheduleStore;
|
||||||
backups: ServerBackupStore;
|
backups: ServerBackupStore;
|
||||||
socket: SocketStore;
|
socket: SocketStore;
|
||||||
status: ServerStatusStore;
|
status: ServerStatusStore;
|
||||||
|
@ -88,6 +90,7 @@ export const ServerContext = createContextStore<ServerStore>({
|
||||||
files,
|
files,
|
||||||
subusers,
|
subusers,
|
||||||
backups,
|
backups,
|
||||||
|
schedules,
|
||||||
clearServerState: action(state => {
|
clearServerState: action(state => {
|
||||||
state.server.data = undefined;
|
state.server.data = undefined;
|
||||||
state.server.permissions = [];
|
state.server.permissions = [];
|
||||||
|
@ -95,7 +98,8 @@ export const ServerContext = createContextStore<ServerStore>({
|
||||||
state.subusers.data = [];
|
state.subusers.data = [];
|
||||||
state.files.directory = '/';
|
state.files.directory = '/';
|
||||||
state.files.contents = [];
|
state.files.contents = [];
|
||||||
state.backups.backups = [];
|
state.backups.data = [];
|
||||||
|
state.schedules.data = [];
|
||||||
|
|
||||||
if (state.socket.instance) {
|
if (state.socket.instance) {
|
||||||
state.socket.instance.removeAllListeners();
|
state.socket.instance.removeAllListeners();
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { action, Action } from 'easy-peasy';
|
||||||
|
import { Schedule } from '@/api/server/schedules/getServerSchedules';
|
||||||
|
|
||||||
|
export interface ServerScheduleStore {
|
||||||
|
data: Schedule[];
|
||||||
|
setSchedules: Action<ServerScheduleStore, Schedule[]>;
|
||||||
|
appendSchedule: Action<ServerScheduleStore, Schedule>;
|
||||||
|
removeSchedule: Action<ServerScheduleStore, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedules: ServerScheduleStore = {
|
||||||
|
data: [],
|
||||||
|
|
||||||
|
setSchedules: action((state, payload) => {
|
||||||
|
state.data = payload;
|
||||||
|
}),
|
||||||
|
|
||||||
|
appendSchedule: action((state, payload) => {
|
||||||
|
if (state.data.find(schedule => schedule.id === payload.id)) {
|
||||||
|
state.data = state.data.map(schedule => schedule.id === payload.id ? payload : schedule);
|
||||||
|
} else {
|
||||||
|
state.data = [ ...state.data, payload ];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
removeSchedule: action((state, payload) => {
|
||||||
|
state.data = [ ...state.data.filter(schedule => schedule.id !== payload) ];
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default schedules;
|
Loading…
Reference in New Issue