export function LatencyCorrelations()

in x-pack/solutions/observability/plugins/apm/public/components/app/correlations/latency_correlations.tsx [67:392]


export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) {
  const {
    core: { notifications },
  } = useApmPluginContext();

  const { euiTheme } = useEuiTheme();

  const { progress, response, startFetch, cancelFetch } = useLatencyCorrelations();
  const { overallHistogram, hasData, status } = getOverallHistogram(response, progress.isRunning);

  useEffect(() => {
    if (progress.error) {
      notifications.toasts.addDanger({
        title: i18n.translate('xpack.apm.correlations.latencyCorrelations.errorTitle', {
          defaultMessage: 'An error occurred fetching correlations',
        }),
        text: progress.error,
      });
    }
  }, [progress.error, notifications.toasts]);

  const [pinnedSignificantTerm, setPinnedSignificantTerm] = useState<LatencyCorrelation | null>(
    null
  );
  const [selectedSignificantTerm, setSelectedSignificantTerm] = useState<LatencyCorrelation | null>(
    null
  );

  const history = useHistory();
  const trackApmEvent = useUiTracker({ app: 'apm' });

  const onAddFilter = useCallback<OnAddFilter>(
    ({ fieldName, fieldValue, include }) => {
      if (include) {
        push(history, {
          query: {
            kuery: `${fieldName}:"${fieldValue}"`,
          },
        });
        trackApmEvent({ metric: 'correlations_term_include_filter' });
      } else {
        push(history, {
          query: {
            kuery: `not ${fieldName}:"${fieldValue}"`,
          },
        });
        trackApmEvent({ metric: 'correlations_term_exclude_filter' });
      }
      onFilter();
    },
    [onFilter, history, trackApmEvent]
  );

  const mlCorrelationColumns: Array<EuiBasicTableColumn<LatencyCorrelation>> = useMemo(
    () => [
      {
        width: '116px',
        field: 'correlation',
        name: (
          <>
            {i18n.translate(
              'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationLabel',
              {
                defaultMessage: 'Correlation',
              }
            )}
            &nbsp;
            <EuiIconTip
              content={i18n.translate(
                'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription',
                {
                  defaultMessage:
                    'The correlation score [0-1] of an attribute; the greater the score, the more an attribute increases latency.',
                }
              )}
              size="s"
              color="subdued"
              type="questionInCircle"
              className="eui-alignTop"
            />
          </>
        ),
        render: (_, { correlation }) => {
          return <div>{asPreciseDecimal(correlation, 2)}</div>;
        },
        sortable: true,
      },
      {
        width: '116px',
        field: 'pValue',
        name: (
          <>
            {i18n.translate(
              'xpack.apm.correlations.failedTransactions.correlationsTable.impactLabel',
              {
                defaultMessage: 'Impact',
              }
            )}
          </>
        ),
        render: (_, { correlation, isFallbackResult }) => {
          const label = getLatencyCorrelationImpactLabel(correlation, isFallbackResult);
          return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
        },
        sortable: true,
      },

      {
        field: 'fieldName',
        name: i18n.translate(
          'xpack.apm.correlations.latencyCorrelations.correlationsTable.fieldNameLabel',
          { defaultMessage: 'Field name' }
        ),
        render: (_, { fieldName, fieldValue }) => (
          <>
            {fieldName}
            <FieldStatsPopover
              fieldName={fieldName}
              fieldValue={fieldValue}
              onAddFilter={onAddFilter}
            />
          </>
        ),
        sortable: true,
      },
      {
        field: 'fieldValue',
        name: i18n.translate(
          'xpack.apm.correlations.latencyCorrelations.correlationsTable.fieldValueLabel',
          { defaultMessage: 'Field value' }
        ),
        render: (_, { fieldValue }) => String(fieldValue).slice(0, 50),
        sortable: true,
      },
      {
        width: '100px',
        actions: [
          {
            name: i18n.translate(
              'xpack.apm.correlations.latencyCorrelations.correlationsTable.filterLabel',
              { defaultMessage: 'Filter' }
            ),
            description: ({ fieldName }) =>
              i18n.translate(
                'xpack.apm.correlations.latencyCorrelations.correlationsTable.filterDescription',
                { defaultMessage: 'Filter by {fieldName}', values: { fieldName } }
              ),
            icon: 'plusInCircle',
            type: 'icon',
            onClick: ({ fieldName, fieldValue }: LatencyCorrelation) =>
              onAddFilter({
                fieldName,
                fieldValue,
                include: true,
              }),
          },
          {
            name: i18n.translate(
              'xpack.apm.correlations.latencyCorrelations.correlationsTable.excludeLabel',
              { defaultMessage: 'Exclude' }
            ),
            description: ({ fieldName }) =>
              i18n.translate(
                'xpack.apm.correlations.latencyCorrelations.correlationsTable.excludeDescription',
                { defaultMessage: 'Filter out {fieldName}', values: { fieldName } }
              ),
            icon: 'minusInCircle',
            type: 'icon',
            onClick: ({ fieldName, fieldValue }: LatencyCorrelation) =>
              onAddFilter({
                fieldName,
                fieldValue,
                include: false,
              }),
          },
        ],
        name: i18n.translate(
          'xpack.apm.correlations.latencyCorrelations.correlationsTable.actionsLabel',
          { defaultMessage: 'Filter' }
        ),
      },
    ],
    [onAddFilter]
  );

  const [sortField, setSortField] = useState<keyof LatencyCorrelation>('correlation');
  const [sortDirection, setSortDirection] = useState<Direction>('desc');

  const onTableChange = useCallback(({ sort }: any) => {
    const { field: currentSortField, direction: currentSortDirection } = sort;

    setSortField(currentSortField);
    setSortDirection(currentSortDirection);
  }, []);

  const sorting: EuiTableSortingType<LatencyCorrelation> = {
    sort: { field: sortField, direction: sortDirection },
  };

  const histogramTerms = useMemo(
    () => orderBy(response.latencyCorrelations ?? [], sortField, sortDirection),
    [response.latencyCorrelations, sortField, sortDirection]
  );

  const selectedHistogram = useMemo(() => {
    if (!histogramTerms) {
      return;
    } else if (selectedSignificantTerm) {
      return histogramTerms?.find(
        (h) =>
          h.fieldName === selectedSignificantTerm.fieldName &&
          h.fieldValue === selectedSignificantTerm.fieldValue
      );
    } else if (pinnedSignificantTerm) {
      return histogramTerms.find(
        (h) =>
          h.fieldName === pinnedSignificantTerm.fieldName &&
          h.fieldValue === pinnedSignificantTerm.fieldValue
      );
    }
    return histogramTerms[0];
  }, [histogramTerms, pinnedSignificantTerm, selectedSignificantTerm]);

  const showCorrelationsTable = progress.isRunning || histogramTerms.length > 0;
  const showCorrelationsEmptyStatePrompt =
    histogramTerms.length < 1 && (progress.loaded === 1 || !progress.isRunning);

  const transactionDistributionChartData = getTransactionDistributionChartData({
    euiTheme,
    allTransactionsHistogram: overallHistogram,
    selectedTerm: selectedHistogram,
  });

  return (
    <div data-test-subj="apmLatencyCorrelationsTabContent">
      <EuiFlexGroup
        css={css`
          min-height: ${MIN_TAB_TITLE_HEIGHT};
        `}
        alignItems="center"
        gutterSize="s"
      >
        <EuiFlexItem grow={false}>
          <EuiTitle size="xs">
            <h5 data-test-subj="apmCorrelationsLatencyCorrelationsChartTitle">
              {i18n.translate('xpack.apm.correlations.latencyCorrelations.panelTitle', {
                defaultMessage: 'Latency distribution',
              })}
            </h5>
          </EuiTitle>
        </EuiFlexItem>

        <EuiFlexItem grow={false}>
          <ChartTitleToolTip />
        </EuiFlexItem>

        <EuiFlexItem>
          <TotalDocCountLabel
            eventType={ProcessorEvent.transaction}
            totalDocCount={response.totalDocCount}
          />
        </EuiFlexItem>

        <EuiFlexItem grow={false}>
          <LatencyCorrelationsHelpPopover />
        </EuiFlexItem>
      </EuiFlexGroup>

      <EuiSpacer size="s" />

      <DurationDistributionChart
        markerValue={response.percentileThresholdValue ?? 0}
        data={transactionDistributionChartData}
        hasData={hasData}
        status={status}
        eventType={ProcessorEvent.transaction}
      />

      <EuiSpacer size="s" />

      <EuiTitle size="xs">
        <h5 data-test-subj="apmCorrelationsLatencyCorrelationsTablePanelTitle">
          {i18n.translate('xpack.apm.correlations.latencyCorrelations.tableTitle', {
            defaultMessage: 'Correlations',
          })}
        </h5>
      </EuiTitle>

      <EuiSpacer size="s" />

      <CorrelationsProgressControls
        progress={progress.loaded}
        isRunning={progress.isRunning}
        onRefresh={startFetch}
        onCancel={cancelFetch}
      />

      {response.ccsWarning && (
        <>
          <EuiSpacer size="m" />
          {/* Latency correlations uses ES aggs that are available since 7.14 */}
          <CrossClusterSearchCompatibilityWarning version="7.14" />
        </>
      )}

      <EuiSpacer size="m" />

      <div data-test-subj="apmCorrelationsTable">
        {showCorrelationsTable && (
          <CorrelationsTable<LatencyCorrelation>
            columns={mlCorrelationColumns}
            rowHeader="correlation"
            significantTerms={histogramTerms}
            status={progress.isRunning ? FETCH_STATUS.LOADING : FETCH_STATUS.SUCCESS}
            setPinnedSignificantTerm={setPinnedSignificantTerm}
            setSelectedSignificantTerm={setSelectedSignificantTerm}
            selectedTerm={selectedHistogram}
            onTableChange={onTableChange}
            sorting={sorting}
          />
        )}
        {showCorrelationsEmptyStatePrompt && <CorrelationsEmptyStatePrompt />}
      </div>
    </div>
  );
}