import LazyLoad from 'react-lazyload';
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import type {Location} from 'history';
import GuideAnchor from 'sentry/components/assistant/guideAnchor';
import Tag from 'sentry/components/badge/tag';
import {LinkButton} from 'sentry/components/button';
import MiniBarChart from 'sentry/components/charts/miniBarChart';
import Count from 'sentry/components/count';
import GlobalSelectionLink from 'sentry/components/globalSelectionLink';
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import Link from 'sentry/components/links/link';
import NotAvailable from 'sentry/components/notAvailable';
import {extractSelectionParameters} from 'sentry/components/organizations/pageFilters/utils';
import PanelItem from 'sentry/components/panels/panelItem';
import Placeholder from 'sentry/components/placeholder';
import {Tooltip} from 'sentry/components/tooltip';
import {IconCheckmark, IconFire, IconWarning} from 'sentry/icons';
import {t, tn} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types/organization';
import type {Release, ReleaseProject} from 'sentry/types/release';
import {defined} from 'sentry/utils';
import type {IconSize} from 'sentry/utils/theme';
import {
ADOPTION_STAGE_LABELS,
displayCrashFreePercent,
getReleaseNewIssuesUrl,
getReleaseUnhandledIssuesUrl,
isMobileRelease,
} from '../../utils';
import {ReleasesDisplayOption} from '../releasesDisplayOptions';
import type {ReleasesRequestRenderProps} from '../releasesRequest';
import {
AdoptionColumn,
AdoptionStageColumn,
CrashFreeRateColumn,
DisplaySmallCol,
NewIssuesColumn,
ReleaseProjectColumn,
ReleaseProjectsLayout,
} from '.';
const CRASH_FREE_DANGER_THRESHOLD = 98;
const CRASH_FREE_WARNING_THRESHOLD = 99.5;
function getCrashFreeIcon(crashFreePercent: number, iconSize: IconSize = 'sm') {
if (crashFreePercent < CRASH_FREE_DANGER_THRESHOLD) {
return ;
}
if (crashFreePercent < CRASH_FREE_WARNING_THRESHOLD) {
return ;
}
return ;
}
type Props = {
activeDisplay: ReleasesDisplayOption;
getHealthData: ReleasesRequestRenderProps['getHealthData'];
index: number;
isTopRelease: boolean;
location: Location;
organization: Organization;
project: ReleaseProject;
releaseVersion: string;
showPlaceholders: boolean;
showReleaseAdoptionStages: boolean;
adoptionStages?: Release['adoptionStages'];
};
function ReleaseCardProjectRow({
activeDisplay,
adoptionStages,
getHealthData,
index,
isTopRelease,
location,
organization,
project,
releaseVersion,
showPlaceholders,
showReleaseAdoptionStages,
}: Props) {
const theme = useTheme();
const {id, newGroups} = project;
const crashCount = getHealthData.getCrashCount(
releaseVersion,
id,
ReleasesDisplayOption.SESSIONS
);
const crashFreeRate = getHealthData.getCrashFreeRate(releaseVersion, id, activeDisplay);
const get24hCountByProject = getHealthData.get24hCountByProject(id, activeDisplay);
const timeSeries = getHealthData.getTimeSeries(releaseVersion, id, activeDisplay);
const adoption = getHealthData.getAdoption(releaseVersion, id, activeDisplay);
const adoptionStage =
showReleaseAdoptionStages &&
adoptionStages?.[project.slug] &&
adoptionStages?.[project.slug].stage;
const adoptionStageLabel =
get24hCountByProject && adoptionStage && isMobileRelease(project.platform)
? ADOPTION_STAGE_LABELS[adoptionStage]
: null;
return (
{showReleaseAdoptionStages && (
{adoptionStageLabel ? (
{adoptionStageLabel.name}
) : (
)}
)}
{showPlaceholders ? (
) : (
{adoption ? Math.round(adoption) : '0'}%
{
const suffix =
activeDisplay === ReleasesDisplayOption.USERS
? tn('user', 'users', value)
: tn('session', 'sessions', value);
return `${value.toLocaleString()} ${suffix}`;
}}
colors={[theme.purple300, theme.gray200]}
/>
)}
{showPlaceholders ? (
) : defined(crashFreeRate) ? (
{getCrashFreeIcon(crashFreeRate)}
{displayCrashFreePercent(crashFreeRate)}
) : (
)}
{showPlaceholders ? (
) : defined(crashCount) ? (
) : (
)}
{t('View')}
);
}
export default ReleaseCardProjectRow;
const ProjectRow = styled(PanelItem)`
padding: ${space(1)} ${space(2)};
@media (min-width: ${p => p.theme.breakpoints.medium}) {
font-size: ${p => p.theme.fontSizeMedium};
}
`;
const StyledPlaceholder = styled(Placeholder)`
height: 15px;
display: inline-block;
position: relative;
top: ${space(0.25)};
`;
const AdoptionWrapper = styled('span')`
flex: 1;
display: inline-grid;
grid-template-columns: 30px 1fr;
gap: ${space(1)};
align-items: center;
/* Chart tooltips need overflow */
overflow: visible;
`;
const CrashFreeWrapper = styled('div')`
display: inline-grid;
grid-auto-flow: column;
grid-column-gap: ${space(1)};
align-items: center;
vertical-align: middle;
`;
const ViewColumn = styled('div')`
${p => p.theme.overflowEllipsis};
line-height: 20px;
text-align: right;
`;