private renderSupervisorTable()

in web-console/src/views/supervisors-view/supervisors-view.tsx [713:1021]


  private renderSupervisorTable() {
    const { filters, onFiltersChange } = this.props;
    const { supervisorsState, statsKey, visibleColumns, page, pageSize, sorted } = this.state;

    const supervisors = supervisorsState.data || [];
    return (
      <ReactTable
        data={supervisors}
        pages={10000000} // Dummy, we are hiding the page selector
        loading={supervisorsState.loading}
        noDataText={
          supervisorsState.isEmpty() ? 'No supervisors' : supervisorsState.getErrorMessage() || ''
        }
        manual
        filterable
        filtered={filters}
        onFilteredChange={onFiltersChange}
        sorted={sorted}
        onSortedChange={sorted => this.setState({ sorted })}
        page={page}
        onPageChange={page => this.setState({ page })}
        pageSize={pageSize}
        onPageSizeChange={pageSize => this.setState({ pageSize })}
        pageSizeOptions={SMALL_TABLE_PAGE_SIZE_OPTIONS}
        showPagination={supervisors.length >= SMALL_TABLE_PAGE_SIZE || page > 0}
        showPageJump={false}
        ofText=""
        columns={[
          {
            Header: twoLines('Supervisor ID', <i>(datasource)</i>),
            id: 'supervisor_id',
            accessor: 'supervisor_id',
            width: 280,
            show: visibleColumns.shown('Supervisor ID'),
            Cell: ({ value, original }) => (
              <TableClickableCell
                tooltip="Show detail"
                onClick={() => this.onSupervisorDetail(original)}
                hoverIcon={IconNames.SEARCH_TEMPLATE}
              >
                {value}
              </TableClickableCell>
            ),
          },
          {
            Header: 'Type',
            accessor: 'type',
            width: 120,
            Cell: this.renderSupervisorFilterableCell('type'),
            show: visibleColumns.shown('Type'),
          },
          {
            Header: 'Topic/Stream',
            accessor: 'source',
            width: 200,
            Cell: this.renderSupervisorFilterableCell('source'),
            show: visibleColumns.shown('Topic/Stream'),
          },
          {
            Header: 'Status',
            id: 'detailed_state',
            width: 170,
            Filter: suggestibleFilterInput([
              'CONNECTING_TO_STREAM',
              'CREATING_TASKS',
              'DISCOVERING_INITIAL_TASKS',
              'IDLE',
              'INVALID_SPEC',
              'LOST_CONTACT_WITH_STREAM',
              'PENDING',
              'RUNNING',
              'SCHEDULER_STOPPED',
              'STOPPING',
              'SUSPENDED',
              'UNABLE_TO_CONNECT_TO_STREAM',
              'UNHEALTHY_SUPERVISOR',
              'UNHEALTHY_TASKS',
            ]),
            accessor: 'detailed_state',
            Cell: ({ value }) => (
              <TableFilterableCell
                field="detailed_state"
                value={value}
                filters={filters}
                onFiltersChange={onFiltersChange}
              >
                <span>
                  <span style={{ color: detailedStateToColor(value) }}>&#x25cf;&nbsp;</span>
                  {value}
                </span>
              </TableFilterableCell>
            ),
            show: visibleColumns.shown('Status'),
          },
          {
            Header: 'Configured tasks',
            id: 'configured_tasks',
            width: 130,
            accessor: 'spec',
            filterable: false,
            sortable: false,
            className: 'padded',
            Cell: ({ value }) => {
              if (!value) return null;
              const taskCount = deepGet(value, 'spec.ioConfig.taskCount');
              const replicas = deepGet(value, 'spec.ioConfig.replicas');
              if (typeof taskCount !== 'number' || typeof replicas !== 'number') return null;
              return (
                <div>
                  <div>{formatInteger(taskCount * replicas)}</div>
                  <div className="detail-line">
                    {replicas === 1
                      ? '(no replication)'
                      : `(${pluralIfNeeded(taskCount, 'task')} × ${pluralIfNeeded(
                          replicas,
                          'replica',
                        )})`}
                  </div>
                </div>
              );
            },
            show: visibleColumns.shown('Configured tasks'),
          },
          {
            Header: 'Running tasks',
            id: 'running_tasks',
            width: 150,
            accessor: 'status.payload',
            filterable: false,
            sortable: false,
            Cell: ({ value, original }) => {
              if (original.suspended) return;
              let label: string | JSX.Element;
              const { activeTasks, publishingTasks } = value || {};
              if (Array.isArray(activeTasks)) {
                label = pluralIfNeeded(activeTasks.length, 'active task');
                if (nonEmptyArray(publishingTasks)) {
                  label = (
                    <>
                      <div>{label}</div>
                      <div>{pluralIfNeeded(publishingTasks.length, 'publishing task')}</div>
                    </>
                  );
                }
              } else {
                label = '';
              }

              return (
                <TableClickableCell
                  tooltip="Go to tasks"
                  onClick={() => this.goToTasksForSupervisor(original)}
                  hoverIcon={IconNames.ARROW_TOP_RIGHT}
                >
                  {label}
                </TableClickableCell>
              );
            },
            show: visibleColumns.shown('Running tasks'),
          },
          {
            Header: 'Aggregate lag',
            accessor: 'status.payload.aggregateLag',
            width: 200,
            filterable: false,
            sortable: false,
            className: 'padded',
            show: visibleColumns.shown('Aggregate lag'),
            Cell: ({ value }) => (isNumberLike(value) ? formatInteger(value) : null),
          },
          {
            Header: twoLines(
              'Stats',
              <Popover
                position={Position.BOTTOM}
                content={
                  <Menu>
                    {ROW_STATS_KEYS.map(k => (
                      <MenuItem
                        key={k}
                        icon={checkedCircleIcon(k === statsKey)}
                        text={getRowStatsKeyTitle(k)}
                        onClick={() => {
                          this.setState({ statsKey: k });
                        }}
                      />
                    ))}
                  </Menu>
                }
              >
                <i className="title-button">
                  {getRowStatsKeyTitle(statsKey)} <Icon icon={IconNames.CARET_DOWN} />
                </i>
              </Popover>,
            ),
            id: 'stats',
            width: 300,
            filterable: false,
            sortable: false,
            className: 'padded',
            accessor: 'stats',
            Cell: ({ value, original }) => {
              if (!value) return;
              const activeTasks: SupervisorStatusTask[] | undefined = deepGet(
                original,
                'status.payload.activeTasks',
              );
              const activeTaskIds: string[] | undefined = Array.isArray(activeTasks)
                ? activeTasks.map((t: SupervisorStatusTask) => t.id)
                : undefined;

              const c = getTotalSupervisorStats(value, statsKey, activeTaskIds);
              const seconds = getRowStatsKeySeconds(statsKey);
              const issues =
                (c.processedWithError || 0) + (c.thrownAway || 0) + (c.unparseable || 0);
              const totalLabel = `Total (past ${statsKey}): `;
              return issues ? (
                <div>
                  <div
                    data-tooltip={`${totalLabel}${formatBytes(c.processedBytes * seconds)}`}
                  >{`Input: ${formatByteRate(c.processedBytes)}`}</div>
                  {Boolean(c.processed) && (
                    <div
                      data-tooltip={`${totalLabel}${formatInteger(c.processed * seconds)} events`}
                    >{`Processed: ${formatRate(c.processed)}`}</div>
                  )}
                  {Boolean(c.processedWithError) && (
                    <div
                      className="warning-line"
                      data-tooltip={`${totalLabel}${formatInteger(
                        c.processedWithError * seconds,
                      )} events`}
                    >
                      Processed with error: {formatRate(c.processedWithError)}
                    </div>
                  )}
                  {Boolean(c.thrownAway) && (
                    <div
                      className="warning-line"
                      data-tooltip={`${totalLabel}${formatInteger(c.thrownAway * seconds)} events`}
                    >
                      Thrown away: {formatRate(c.thrownAway)}
                    </div>
                  )}
                  {Boolean(c.unparseable) && (
                    <div
                      className="warning-line"
                      data-tooltip={`${totalLabel}${formatInteger(c.unparseable * seconds)} events`}
                    >
                      Unparseable: {formatRate(c.unparseable)}
                    </div>
                  )}
                </div>
              ) : c.processedBytes ? (
                <div
                  data-tooltip={`${totalLabel}${formatInteger(
                    c.processed * seconds,
                  )} events, ${formatBytes(c.processedBytes * seconds)}`}
                >{`Processed: ${formatRate(c.processed)} (${formatByteRate(
                  c.processedBytes,
                )})`}</div>
              ) : (
                <div>No activity</div>
              );
            },
            show: visibleColumns.shown('Stats'),
          },
          {
            Header: 'Recent errors',
            accessor: 'status.payload.recentErrors',
            width: 150,
            filterable: false,
            sortable: false,
            show: visibleColumns.shown('Recent errors'),
            Cell: ({ value, original }) => {
              if (!value) return null;
              return (
                <TableClickableCell
                  tooltip="Show errors"
                  onClick={() => this.onSupervisorDetail(original)}
                  hoverIcon={IconNames.SEARCH_TEMPLATE}
                >
                  {pluralIfNeeded(value.length, 'error')}
                </TableClickableCell>
              );
            },
          },
          {
            Header: ACTION_COLUMN_LABEL,
            id: ACTION_COLUMN_ID,
            accessor: 'supervisor_id',
            width: ACTION_COLUMN_WIDTH,
            filterable: false,
            sortable: false,
            Cell: ({ value: id, original }) => {
              const supervisorActions = this.getSupervisorActions(original);
              return (
                <ActionCell
                  onDetail={() => this.onSupervisorDetail(original)}
                  actions={supervisorActions}
                  menuTitle={id}
                />
              );
            },
          },
        ]}
      />
    );
  }