github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/cluster/containers/events/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 _ from "lodash";
    12  import moment from "moment";
    13  import React from "react";
    14  import { Helmet } from "react-helmet";
    15  import { Link, withRouter } from "react-router-dom";
    16  import { connect } from "react-redux";
    17  import * as protos from "src/js/protos";
    18  import { refreshEvents } from "src/redux/apiReducers";
    19  import { eventsSelector, eventsValidSelector } from "src/redux/events";
    20  import { LocalSetting } from "src/redux/localsettings";
    21  import { AdminUIState } from "src/redux/state";
    22  import { TimestampToMoment } from "src/util/convert";
    23  import { getEventDescription } from "src/util/events";
    24  import { DATE_FORMAT } from "src/util/format";
    25  import { SortSetting } from "src/views/shared/components/sortabletable";
    26  import { SortedTable } from "src/views/shared/components/sortedtable";
    27  import { ToolTipWrapper } from "src/views/shared/components/toolTip";
    28  import "./events.styl";
    29  
    30  type Event$Properties = protos.cockroach.server.serverpb.EventsResponse.IEvent;
    31  
    32  // Number of events to show in the sidebar.
    33  const EVENT_BOX_NUM_EVENTS = 5;
    34  
    35  const eventsSortSetting = new LocalSetting<AdminUIState, SortSetting>(
    36    "events/sort_setting", (s) => s.localSettings,
    37  );
    38  
    39  export interface SimplifiedEvent {
    40     // How long ago the event occurred  (e.g. "10 minutes ago").
    41    fromNowString: string;
    42    sortableTimestamp: moment.Moment;
    43    content: React.ReactNode;
    44  }
    45  
    46  class EventSortedTable extends SortedTable<SimplifiedEvent> {}
    47  
    48  export interface EventRowProps {
    49    event: Event$Properties;
    50  }
    51  
    52  export function getEventInfo(e: Event$Properties): SimplifiedEvent {
    53    return {
    54      fromNowString: TimestampToMoment(e.timestamp).format(DATE_FORMAT)
    55        .replace("second", "sec")
    56        .replace("minute", "min"),
    57      content: <span>{ getEventDescription(e) }</span>,
    58      sortableTimestamp: TimestampToMoment(e.timestamp),
    59    };
    60  }
    61  
    62  export class EventRow extends React.Component<EventRowProps, {}> {
    63    render() {
    64      const { event } = this.props;
    65      const e = getEventInfo(event);
    66      return <tr>
    67        <td>
    68          <ToolTipWrapper
    69            placement="left"
    70            text={ e.content }
    71          >
    72            <div className="events__message">
    73              {e.content}
    74            </div>
    75          </ToolTipWrapper>
    76          <div className="events__timestamp">
    77            {e.fromNowString}
    78          </div>
    79        </td>
    80      </tr>;
    81    }
    82  }
    83  
    84  export interface EventBoxProps {
    85    events: Event$Properties[];
    86    // eventsValid is needed so that this component will re-render when the events
    87    // data becomes invalid, and thus trigger a refresh.
    88    eventsValid: boolean;
    89    refreshEvents: typeof refreshEvents;
    90  }
    91  
    92  export class EventBoxUnconnected extends React.Component<EventBoxProps, {}> {
    93  
    94    componentDidMount() {
    95      // Refresh events when mounting.
    96      this.props.refreshEvents();
    97    }
    98  
    99    componentDidUpdate() {
   100      // Refresh events when props change.
   101      this.props.refreshEvents();
   102    }
   103  
   104    render() {
   105      const events = this.props.events;
   106      return <div className="events">
   107        <table>
   108          <tbody>
   109            {_.map(_.take(events, EVENT_BOX_NUM_EVENTS), (e: Event$Properties, i: number) => {
   110              return <EventRow event={e} key={i} />;
   111            })}
   112            <tr>
   113              <td className="events__more-link" colSpan={2}><Link to="/events">View all events</Link></td>
   114            </tr>
   115          </tbody>
   116        </table>
   117      </div>;
   118    }
   119  }
   120  
   121  export interface EventPageProps {
   122    events: Event$Properties[];
   123    // eventsValid is needed so that this component will re-render when the events
   124    // data becomes invalid, and thus trigger a refresh.
   125    eventsValid: boolean;
   126    refreshEvents: typeof refreshEvents;
   127    sortSetting: SortSetting;
   128    setSort: typeof eventsSortSetting.set;
   129  }
   130  
   131  export class EventPageUnconnected extends React.Component<EventPageProps, {}> {
   132    componentDidMount() {
   133      // Refresh events when mounting.
   134      this.props.refreshEvents();
   135    }
   136  
   137    componentDidUpdate() {
   138      // Refresh events when props change.
   139      this.props.refreshEvents();
   140    }
   141  
   142    render() {
   143      const { events, sortSetting } = this.props;
   144  
   145      const simplifiedEvents = _.map(events, getEventInfo);
   146  
   147      return <div>
   148        <Helmet title="Events" />
   149        <section className="section section--heading">
   150          <h1 className="base-heading">Events</h1>
   151        </section>
   152        <section className="section l-columns">
   153          <div className="l-columns__left events-table">
   154            <EventSortedTable
   155              data={simplifiedEvents}
   156              sortSetting={sortSetting}
   157              onChangeSortSetting={(setting) => this.props.setSort(setting)}
   158              columns={[
   159                {
   160                  title: "Event",
   161                  cell: (e) => e.content,
   162                },
   163                {
   164                  title: "Timestamp",
   165                  cell: (e) => e.fromNowString,
   166                  sort: (e) => e.sortableTimestamp,
   167                },
   168              ]}
   169              />
   170          </div>
   171        </section>
   172      </div>;
   173    }
   174  }
   175  
   176  // Connect the EventsList class with our redux store.
   177  const eventBoxConnected = withRouter(connect(
   178    (state: AdminUIState) => {
   179      return {
   180        events: eventsSelector(state),
   181        eventsValid: eventsValidSelector(state),
   182      };
   183    },
   184    {
   185      refreshEvents,
   186    },
   187  )(EventBoxUnconnected));
   188  
   189  // Connect the EventsList class with our redux store.
   190  const eventPageConnected = withRouter(connect(
   191    (state: AdminUIState) => {
   192      return {
   193        events: eventsSelector(state),
   194        eventsValid: eventsValidSelector(state),
   195        sortSetting: eventsSortSetting.selector(state),
   196      };
   197    },
   198    {
   199      refreshEvents,
   200      setSort: eventsSortSetting.set,
   201    },
   202  )(EventPageUnconnected));
   203  
   204  export { eventBoxConnected as EventBox };
   205  export { eventPageConnected as EventPage };