import {Component, Fragment} from 'react'; import LazyLoad from 'react-lazyload'; import {WithRouterProps} from 'react-router'; import {useSortable} from '@dnd-kit/sortable'; import styled from '@emotion/styled'; import {Location} from 'history'; import {Client} from 'sentry/api'; import {Alert} from 'sentry/components/alert'; import {Button} from 'sentry/components/button'; import ErrorPanel from 'sentry/components/charts/errorPanel'; import {HeaderTitle} from 'sentry/components/charts/styles'; import ErrorBoundary from 'sentry/components/errorBoundary'; import ExternalLink from 'sentry/components/links/externalLink'; import Panel from 'sentry/components/panels/panel'; import PanelAlert from 'sentry/components/panels/panelAlert'; import Placeholder from 'sentry/components/placeholder'; import {parseSearch} from 'sentry/components/searchSyntax/parser'; import {Tooltip} from 'sentry/components/tooltip'; import {IconCopy, IconDelete, IconEdit, IconGrabbable, IconWarning} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {Organization, PageFilters} from 'sentry/types'; import {Series} from 'sentry/types/echarts'; import {getFormattedDate} from 'sentry/utils/dates'; import {TableDataWithTitle} from 'sentry/utils/discover/discoverQuery'; import {AggregationOutputType, parseFunction} from 'sentry/utils/discover/fields'; import { MEPConsumer, MEPState, } from 'sentry/utils/performance/contexts/metricsEnhancedSetting'; import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry'; import withApi from 'sentry/utils/withApi'; import withOrganization from 'sentry/utils/withOrganization'; import withPageFilters from 'sentry/utils/withPageFilters'; // eslint-disable-next-line no-restricted-imports import withSentryRouter from 'sentry/utils/withSentryRouter'; import {DRAG_HANDLE_CLASS} from '../dashboard'; import {DashboardFilters, DisplayType, Widget, WidgetType} from '../types'; import {DEFAULT_RESULTS_LIMIT} from '../widgetBuilder/utils'; import {DashboardsMEPConsumer, DashboardsMEPProvider} from './dashboardsMEPContext'; import WidgetCardChartContainer from './widgetCardChartContainer'; import WidgetCardContextMenu from './widgetCardContextMenu'; const SESSION_DURATION_INGESTION_STOP_DATE = new Date('2023-01-12'); export const SESSION_DURATION_ALERT = ( {t( 'session.duration is no longer being recorded as of %s. Data in this widget may be incomplete.', getFormattedDate(SESSION_DURATION_INGESTION_STOP_DATE, 'MMM D, YYYY') )} ); type DraggableProps = Pick, 'attributes' | 'listeners'>; type Props = WithRouterProps & { api: Client; isEditing: boolean; location: Location; organization: Organization; selection: PageFilters; widget: Widget; widgetLimitReached: boolean; dashboardFilters?: DashboardFilters; draggableProps?: DraggableProps; hideToolbar?: boolean; index?: string; isMobile?: boolean; isPreview?: boolean; isWidgetInvalid?: boolean; noDashboardsMEPProvider?: boolean; noLazyLoad?: boolean; onDelete?: () => void; onDuplicate?: () => void; onEdit?: () => void; renderErrorMessage?: (errorMessage?: string) => React.ReactNode; showContextMenu?: boolean; showStoredAlert?: boolean; tableItemLimit?: number; windowWidth?: number; }; type State = { pageLinks?: string; seriesData?: Series[]; seriesResultsType?: Record; tableData?: TableDataWithTitle[]; totalIssuesCount?: string; }; type SearchFilterKey = {key?: {value: string}}; const ERROR_FIELDS = [ 'error.handled', 'error.unhandled', 'error.mechanism', 'error.type', 'error.value', ]; class WidgetCard extends Component { state: State = {}; renderToolbar() { const { onEdit, onDelete, onDuplicate, draggableProps, hideToolbar, isEditing, isMobile, } = this.props; if (!isEditing) { return null; } return ( {!isMobile && ( } borderless className={DRAG_HANDLE_CLASS} {...draggableProps?.listeners} {...draggableProps?.attributes} /> )}