import {useCallback, useMemo} from 'react';
import styled from '@emotion/styled';
import {Alert} from 'sentry/components/alert';
import FeatureBadge from 'sentry/components/badge/featureBadge';
import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
import * as Layout from 'sentry/components/layouts/thirds';
import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {browserHistory} from 'sentry/utils/browserHistory';
import {decodeInteger} from 'sentry/utils/queryString';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {ExploreContent} from 'sentry/views/explore/content';
import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout';
import {usePageParams} from './hooks/usePageParams';
import {useTraces} from './hooks/useTraces';
import {TracesChart} from './tracesChart';
import {TracesSearchBar} from './tracesSearchBar';
import {TracesTable} from './tracesTable';
import {normalizeTraces} from './utils';
const TRACE_EXPLORER_DOCS_URL = 'https://docs.sentry.io/product/explore/traces/';
const DEFAULT_STATS_PERIOD = '24h';
const DEFAULT_PER_PAGE = 50;
export default function Wrapper(props) {
const location = useLocation();
const organization = useOrganization();
if (
location.query.view !== 'trace' &&
organization.features.includes('visibility-explore-view')
) {
return ;
}
return ;
}
function Content() {
const location = useLocation();
const organization = useOrganization();
const limit = useMemo(() => {
return decodeInteger(location.query.perPage, DEFAULT_PER_PAGE);
}, [location.query.perPage]);
const {queries} = usePageParams(location);
const handleSearch = useCallback(
(searchIndex: number, searchQuery: string) => {
const newQueries = [...queries];
if (newQueries.length === 0) {
// In the odd case someone wants to add search bars before any query has been made, we add both the default one shown and a new one.
newQueries[0] = '';
}
newQueries[searchIndex] = searchQuery;
browserHistory.push({
...location,
query: {
...location.query,
cursor: undefined,
query: typeof searchQuery === 'string' ? newQueries : queries,
},
});
},
[location, queries]
);
const handleClearSearch = useCallback(
(searchIndex: number) => {
const newQueries = [...queries];
if (typeof newQueries[searchIndex] !== undefined) {
delete newQueries[searchIndex];
browserHistory.push({
...location,
query: {
...location.query,
cursor: undefined,
query: newQueries,
},
});
return true;
}
return false;
},
[location, queries]
);
const tracesQuery = useTraces({
limit,
query: queries,
});
const isLoading = tracesQuery.isFetching;
const isError = !isLoading && tracesQuery.isError;
const isEmpty = !isLoading && !isError && (tracesQuery?.data?.data?.length ?? 0) === 0;
const rawData = !isLoading && !isError ? tracesQuery?.data?.data : undefined;
const data = normalizeTraces(rawData);
return (
{t('Traces')}
{isError && typeof tracesQuery.error?.responseJSON?.detail === 'string' ? (
{tracesQuery.error?.responseJSON?.detail}
) : null}
);
}
const HeaderContentBar = styled('div')`
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: row;
`;
const LayoutMain = styled(Layout.Main)`
display: flex;
flex-direction: column;
gap: ${space(2)};
`;
const StyledAlert = styled(Alert)`
margin-bottom: 0;
`;