1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useCallback , useEffect , useState } from 'react' ;
22import { RouteComponentProps } from 'react-router-dom' ;
33import { Schedule } from '@/api/server/schedules/getServerSchedules' ;
44import getServerSchedule from '@/api/server/schedules/getServerSchedule' ;
55import Spinner from '@/components/elements/Spinner' ;
66import FlashMessageRender from '@/components/FlashMessageRender' ;
77import { httpErrorToHuman } from '@/api/http' ;
8- import ScheduleRow from '@/components/server/schedules/ScheduleRow' ;
9- import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow' ;
108import EditScheduleModal from '@/components/server/schedules/EditScheduleModal' ;
119import NewTaskButton from '@/components/server/schedules/NewTaskButton' ;
1210import DeleteScheduleButton from '@/components/server/schedules/DeleteScheduleButton' ;
@@ -16,7 +14,10 @@ import { ServerContext } from '@/state/server';
1614import PageContentBlock from '@/components/elements/PageContentBlock' ;
1715import tw from 'twin.macro' ;
1816import Button from '@/components/elements/Button' ;
19- import GreyRowBox from '@/components/elements/GreyRowBox' ;
17+ import ScheduleTaskRow from '@/components/server/schedules/ScheduleTaskRow' ;
18+ import isEqual from 'react-fast-compare' ;
19+ import { format } from 'date-fns' ;
20+ import ScheduleCronRow from '@/components/server/schedules/ScheduleCronRow' ;
2021
2122interface Params {
2223 id : string ;
@@ -26,6 +27,24 @@ interface State {
2627 schedule ?: Schedule ;
2728}
2829
30+ const CronBox = ( { title, value } : { title : string ; value : string } ) => (
31+ < div css = { tw `bg-neutral-700 rounded p-4` } >
32+ < p css = { tw `text-neutral-300 text-sm` } > { title } </ p >
33+ < p css = { tw `text-2xl font-medium text-neutral-100` } > { value } </ p >
34+ </ div >
35+ ) ;
36+
37+ const ActivePill = ( { active } : { active : boolean } ) => (
38+ < span
39+ css = { [
40+ tw `rounded-full px-2 py-px text-xs ml-4 uppercase` ,
41+ active ? tw `bg-green-600 text-green-100` : tw `bg-red-600 text-red-100` ,
42+ ] }
43+ >
44+ { active ? 'Active' : 'Inactive' }
45+ </ span >
46+ ) ;
47+
2948export default ( { match, history, location : { state } } : RouteComponentProps < Params , Record < string , unknown > , State > ) => {
3049 const id = ServerContext . useStoreState ( state => state . server . data ! . id ) ;
3150 const uuid = ServerContext . useStoreState ( state => state . server . data ! . uuid ) ;
@@ -34,7 +53,8 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
3453 const [ isLoading , setIsLoading ] = useState ( true ) ;
3554 const [ showEditModal , setShowEditModal ] = useState ( false ) ;
3655
37- const schedule = ServerContext . useStoreState ( st => st . schedules . data . find ( s => s . id === state . schedule ?. id ) , [ match ] ) ;
56+ // @ts -ignore
57+ const schedule : Schedule | undefined = ServerContext . useStoreState ( st => st . schedules . data . find ( s => s . id === state . schedule ?. id ) , isEqual ) ;
3858 const appendSchedule = ServerContext . useStoreActions ( actions => actions . schedules . appendSchedule ) ;
3959
4060 useEffect ( ( ) => {
@@ -53,59 +73,79 @@ export default ({ match, history, location: { state } }: RouteComponentProps<Par
5373 . then ( ( ) => setIsLoading ( false ) ) ;
5474 } , [ match ] ) ;
5575
76+ const toggleEditModal = useCallback ( ( ) => {
77+ setShowEditModal ( s => ! s ) ;
78+ } , [ ] ) ;
79+
5680 return (
5781 < PageContentBlock >
5882 < FlashMessageRender byKey = { 'schedules' } css = { tw `mb-4` } />
5983 { ! schedule || isLoading ?
6084 < Spinner size = { 'large' } centered />
6185 :
6286 < >
63- < GreyRowBox css = { tw `cursor-pointer mb-2 flex-wrap` } >
64- < ScheduleRow schedule = { schedule } />
65- </ GreyRowBox >
66- < EditScheduleModal
67- visible = { showEditModal }
68- schedule = { schedule }
69- onDismissed = { ( ) => setShowEditModal ( false ) }
70- />
71- < div css = { tw `flex items-center mt-8 mb-4` } >
72- < div css = { tw `flex-1` } >
73- < h2 css = { tw `text-2xl` } > Configured Tasks</ h2 >
74- </ div >
87+ < ScheduleCronRow cron = { schedule . cron } css = { tw `sm:hidden bg-neutral-700 rounded mb-4 p-3` } />
88+ < div css = { tw `hidden sm:grid grid-cols-5 md:grid-cols-7 gap-4 mb-6` } >
89+ < CronBox title = { 'Minute' } value = { schedule . cron . minute } />
90+ < CronBox title = { 'Hour' } value = { schedule . cron . hour } />
91+ < CronBox title = { 'Day (Month)' } value = { schedule . cron . dayOfMonth } />
92+ < CronBox title = { 'Month' } value = { '*' } />
93+ < CronBox title = { 'Day (Week)' } value = { schedule . cron . dayOfWeek } />
7594 </ div >
76- { schedule . tasks . length > 0 ?
77- < >
78- {
79- schedule . tasks
80- . sort ( ( a , b ) => a . sequenceId - b . sequenceId )
81- . map ( task => (
82- < ScheduleTaskRow key = { task . id } task = { task } schedule = { schedule } />
83- ) )
84- }
85- { schedule . tasks . length > 1 &&
86- < p css = { tw `text-xs text-neutral-400` } >
87- Task delays are relative to the previous task in the listing.
88- </ p >
95+ < div css = { tw `rounded shadow` } >
96+ < div css = { tw `sm:flex items-center bg-neutral-900 p-3 sm:p-6 border-b-4 border-neutral-600 rounded-t` } >
97+ < div css = { tw `flex-1` } >
98+ < h3 css = { tw `flex items-center text-neutral-100 text-2xl` } >
99+ { schedule . name }
100+ { schedule . isProcessing ?
101+ < span
102+ css = { tw `flex items-center rounded-full px-2 py-px text-xs ml-4 uppercase bg-neutral-600 text-white` }
103+ >
104+ < Spinner css = { tw `w-3! h-3! mr-2` } />
105+ Processing
106+ </ span >
107+ :
108+ < ActivePill active = { schedule . isActive } />
109+ }
110+ </ h3 >
111+ < p css = { tw `mt-1 text-sm text-neutral-300` } >
112+ Last run at:
113+ { schedule . lastRunAt ? format ( schedule . lastRunAt , 'MMM do \'at\' h:mma' ) : 'never' }
114+ </ p >
115+ </ div >
116+ < div css = { tw `flex sm:block mt-3 sm:mt-0` } >
117+ < Can action = { 'schedule.update' } >
118+ < Button
119+ isSecondary
120+ color = { 'grey' }
121+ size = { 'small' }
122+ css = { tw `flex-1 mr-4 border-transparent` }
123+ onClick = { toggleEditModal }
124+ >
125+ Edit
126+ </ Button >
127+ < NewTaskButton schedule = { schedule } />
128+ </ Can >
129+ </ div >
130+ </ div >
131+ < div css = { tw `bg-neutral-700 rounded-b` } >
132+ { schedule . tasks . length > 0 ?
133+ schedule . tasks . map ( task => (
134+ < ScheduleTaskRow key = { `${ schedule . id } _${ task . id } ` } task = { task } schedule = { schedule } />
135+ ) )
136+ :
137+ null
89138 }
90- </ >
91- :
92- < p css = { tw `text-sm text-neutral-400` } >
93- There are no tasks configured for this schedule.
94- </ p >
95- }
96- < div css = { tw `mt-8 flex justify-end` } >
139+ </ div >
140+ </ div >
141+ < EditScheduleModal visible = { showEditModal } schedule = { schedule } onDismissed = { toggleEditModal } />
142+ < div css = { tw `mt-6 flex sm:justify-end` } >
97143 < Can action = { 'schedule.delete' } >
98144 < DeleteScheduleButton
99145 scheduleId = { schedule . id }
100146 onDeleted = { ( ) => history . push ( `/server/${ id } /schedules` ) }
101147 />
102148 </ Can >
103- < Can action = { 'schedule.update' } >
104- < Button css = { tw `mr-4` } onClick = { ( ) => setShowEditModal ( true ) } >
105- Edit
106- </ Button >
107- < NewTaskButton schedule = { schedule } />
108- </ Can >
109149 </ div >
110150 </ >
111151 }
0 commit comments