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));