github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/jobs/index.tsx (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import moment from "moment"; 12 import React from "react"; 13 import { Helmet } from "react-helmet"; 14 import { connect } from "react-redux"; 15 import { withRouter } from "react-router-dom"; 16 17 import { cockroach } from "src/js/protos"; 18 import { jobsKey, refreshJobs } from "src/redux/apiReducers"; 19 import { CachedDataReducerState } from "src/redux/cachedDataReducer"; 20 import { LocalSetting } from "src/redux/localsettings"; 21 import { AdminUIState } from "src/redux/state"; 22 import Dropdown, { DropdownOption } from "src/views/shared/components/dropdown"; 23 import Loading from "src/views/shared/components/loading"; 24 import { PageConfig, PageConfigItem } from "src/views/shared/components/pageconfig"; 25 import { SortSetting} from "src/views/shared/components/sortabletable"; 26 import "./index.styl"; 27 import { statusOptions } from "./jobStatusOptions"; 28 import { JobTable } from "src/views/jobs/jobTable"; 29 import { trackFilter } from "src/util/analytics"; 30 import JobType = cockroach.sql.jobs.jobspb.Type; 31 import JobsRequest = cockroach.server.serverpb.JobsRequest; 32 import JobsResponse = cockroach.server.serverpb.JobsResponse; 33 34 export const statusSetting = new LocalSetting<AdminUIState, string>( 35 "jobs/status_setting", s => s.localSettings, statusOptions[0].value, 36 ); 37 38 const typeOptions = [ 39 { value: JobType.UNSPECIFIED.toString(), label: "All" }, 40 { value: JobType.BACKUP.toString(), label: "Backups" }, 41 { value: JobType.RESTORE.toString(), label: "Restores" }, 42 { value: JobType.IMPORT.toString(), label: "Imports" }, 43 { value: JobType.SCHEMA_CHANGE.toString(), label: "Schema Changes" }, 44 { value: JobType.CHANGEFEED.toString(), label: "Changefeed"}, 45 { value: JobType.CREATE_STATS.toString(), label: "Statistics Creation"}, 46 { value: JobType.AUTO_CREATE_STATS.toString(), label: "Auto-Statistics Creation"}, 47 ]; 48 49 export const typeSetting = new LocalSetting<AdminUIState, number>( 50 "jobs/type_setting", s => s.localSettings, JobType.UNSPECIFIED, 51 ); 52 53 const showOptions = [ 54 { value: "50", label: "Latest 50" }, 55 { value: "0", label: "All" }, 56 ]; 57 58 export const showSetting = new LocalSetting<AdminUIState, string>( 59 "jobs/show_setting", s => s.localSettings, showOptions[0].value, 60 ); 61 62 // Moment cannot render durations (moment/moment#1048). Hack it ourselves. 63 export const formatDuration = (d: moment.Duration) => 64 [Math.floor(d.asHours()).toFixed(0), d.minutes(), d.seconds()] 65 .map(c => ("0" + c).slice(-2)) 66 .join(":"); 67 68 export const sortSetting = new LocalSetting<AdminUIState, SortSetting>( 69 "jobs/sort_setting", 70 s => s.localSettings, 71 { sortKey: 3 /* creation time */, ascending: false }, 72 ); 73 74 interface JobsTableProps { 75 sort: SortSetting; 76 status: string; 77 show: string; 78 type: number; 79 setSort: (value: SortSetting) => void; 80 setStatus: (value: string) => void; 81 setShow: (value: string) => void; 82 setType: (value: JobType) => void; 83 refreshJobs: typeof refreshJobs; 84 jobs: CachedDataReducerState<JobsResponse>; 85 } 86 87 export class JobsTable extends React.Component<JobsTableProps> { 88 refresh(props = this.props) { 89 props.refreshJobs(new JobsRequest({ 90 status: props.status, 91 type: props.type, 92 limit: parseInt(props.show, 10), 93 })); 94 } 95 96 componentDidMount() { 97 this.refresh(); 98 } 99 100 componentDidUpdate() { 101 this.refresh(this.props); 102 } 103 104 onStatusSelected = (selected: DropdownOption) => { 105 const filter = (selected.value === "") ? "all" : selected.value; 106 trackFilter("Status", filter); 107 this.props.setStatus(selected.value); 108 } 109 110 onTypeSelected = (selected: DropdownOption) => { 111 const type = parseInt(selected.value, 10); 112 const typeLabel = typeOptions[type].label; 113 trackFilter("Type", typeLabel); 114 this.props.setType(type); 115 } 116 117 onShowSelected = (selected: DropdownOption) => { 118 this.props.setShow(selected.value); 119 } 120 121 render() { 122 return ( 123 <div className="jobs-page"> 124 <Helmet title="Jobs" /> 125 <section className="section"> 126 <h1 className="base-heading"> 127 Jobs 128 </h1> 129 </section> 130 <div> 131 <PageConfig> 132 <PageConfigItem> 133 <Dropdown 134 title="Status" 135 options={statusOptions} 136 selected={this.props.status} 137 onChange={this.onStatusSelected} 138 /> 139 </PageConfigItem> 140 <PageConfigItem> 141 <Dropdown 142 title="Type" 143 options={typeOptions} 144 selected={this.props.type.toString()} 145 onChange={this.onTypeSelected} 146 /> 147 </PageConfigItem> 148 <PageConfigItem> 149 <Dropdown 150 title="Show" 151 options={showOptions} 152 selected={this.props.show} 153 onChange={this.onShowSelected} 154 /> 155 </PageConfigItem> 156 </PageConfig> 157 </div> 158 <section className="section"> 159 <Loading 160 loading={!this.props.jobs || !this.props.jobs.data} 161 error={this.props.jobs && this.props.jobs.lastError} 162 render={() => <JobTable isUsedFilter={this.props.status.length > 0 || this.props.type > 0} jobs={this.props.jobs} setSort={this.props.setSort} sort={this.props.sort}/>} 163 /> 164 </section> 165 </div> 166 ); 167 } 168 } 169 170 const mapStateToProps = (state: AdminUIState) => { 171 const sort = sortSetting.selector(state); 172 const status = statusSetting.selector(state); 173 const show = showSetting.selector(state); 174 const type = typeSetting.selector(state); 175 const key = jobsKey(status, type, parseInt(show, 10)); 176 const jobs = state.cachedData.jobs[key]; 177 return { 178 sort, status, show, type, jobs, 179 }; 180 }; 181 182 const mapDispatchToProps = { 183 setSort: sortSetting.set, 184 setStatus: statusSetting.set, 185 setShow: showSetting.set, 186 setType: typeSetting.set, 187 refreshJobs, 188 }; 189 190 export default withRouter(connect(mapStateToProps, mapDispatchToProps)(JobsTable));