import styled from '@emotion/styled';
import type {Location} from 'history';
import pick from 'lodash/pick';
import AlertBadge from 'sentry/components/alertBadge';
import {SectionHeading} from 'sentry/components/charts/styles';
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
import Link from 'sentry/components/links/link';
import LoadingError from 'sentry/components/loadingError';
import Placeholder from 'sentry/components/placeholder';
import TimeSince from 'sentry/components/timeSince';
import {URL_PARAM} from 'sentry/constants/pageFilters';
import {IconCheckmark, IconExclamation, IconFire, IconOpen} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types';
import {useApiQuery} from 'sentry/utils/queryClient';
import type {Incident} from 'sentry/views/alerts/types';
import {IncidentStatus} from 'sentry/views/alerts/types';
import MissingAlertsButtons from './missingFeatureButtons/missingAlertsButtons';
import {SectionHeadingLink, SectionHeadingWrapper, SidebarSection} from './styles';
const PLACEHOLDER_AND_EMPTY_HEIGHT = '172px';
interface AlertRowProps {
alert: Incident;
orgSlug: string;
}
function AlertRow({alert, orgSlug}: AlertRowProps) {
const {status, identifier, title, dateClosed, dateStarted} = alert;
const isResolved = status === IncidentStatus.CLOSED;
const isWarning = status === IncidentStatus.WARNING;
const Icon = isResolved ? IconCheckmark : isWarning ? IconExclamation : IconFire;
const statusProps = {isResolved, isWarning};
return (
{title}
{isResolved ? t('Resolved') : t('Triggered')}{' '}
{isResolved ? (
dateClosed ? (
) : null
) : (
)}
);
}
interface ProjectLatestAlertsProps {
isProjectStabilized: boolean;
location: Location;
organization: Organization;
projectSlug: string;
}
function ProjectLatestAlerts({
location,
organization,
isProjectStabilized,
projectSlug,
}: ProjectLatestAlertsProps) {
const query = {
...pick(location.query, Object.values(URL_PARAM)),
per_page: 3,
};
const {
data: unresolvedAlerts = [],
isLoading: unresolvedAlertsIsLoading,
isError: unresolvedAlertsIsError,
} = useApiQuery(
[
`/organizations/${organization.slug}/incidents/`,
{query: {...query, status: 'open'}},
],
{staleTime: 0, enabled: isProjectStabilized}
);
const {
data: resolvedAlerts = [],
isLoading: resolvedAlertsIsLoading,
isError: resolvedAlertsIsError,
} = useApiQuery(
[
`/organizations/${organization.slug}/incidents/`,
{query: {...query, status: 'closed'}},
],
{staleTime: 0, enabled: isProjectStabilized}
);
const alertsUnresolvedAndResolved = [...unresolvedAlerts, ...resolvedAlerts];
const shouldLoadAlertRules =
alertsUnresolvedAndResolved.length === 0 &&
!unresolvedAlertsIsLoading &&
!resolvedAlertsIsLoading;
// This is only used to determine if we should show the "Create Alert" button
const {data: alertRules = [], isLoading: alertRulesLoading} = useApiQuery(
[
`/organizations/${organization.slug}/alert-rules/`,
{
query: {
...pick(location.query, Object.values(URL_PARAM)),
// Sort by name
asc: 1,
per_page: 1,
},
},
],
{
staleTime: 0,
enabled: shouldLoadAlertRules,
}
);
function renderAlertRules() {
if (unresolvedAlertsIsError || resolvedAlertsIsError) {
return ;
}
const isLoading = unresolvedAlertsIsLoading || resolvedAlertsIsLoading;
if (isLoading || (shouldLoadAlertRules && alertRulesLoading)) {
return ;
}
const hasAlertRule = alertsUnresolvedAndResolved.length > 0 || alertRules?.length > 0;
if (!hasAlertRule) {
return (
);
}
if (alertsUnresolvedAndResolved.length === 0) {
return (
{t('No alerts found')}
);
}
return alertsUnresolvedAndResolved
.slice(0, 3)
.map(alert => (
));
}
return (
{t('Latest Alerts')}
{/* as this is a link to latest alerts, we want to only preserve project and environment */}
{renderAlertRules()}
);
}
const AlertRowLink = styled(Link)`
display: flex;
align-items: center;
height: 40px;
margin-bottom: ${space(3)};
margin-left: ${space(0.5)};
&,
&:hover,
&:focus {
color: inherit;
}
&:first-child {
margin-top: ${space(1)};
}
`;
type StatusColorProps = {
isResolved: boolean;
isWarning: boolean;
};
const getStatusColor = ({isResolved, isWarning}: StatusColorProps) =>
isResolved ? 'successText' : isWarning ? 'warningText' : 'errorText';
const AlertBadgeWrapper = styled('div')<{icon: React.ReactNode} & StatusColorProps>`
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
/* icon warning needs to be treated differently to look visually centered */
line-height: ${p => (p.icon === IconExclamation ? undefined : 1)};
`;
const AlertDetails = styled('div')`
font-size: ${p => p.theme.fontSizeMedium};
margin-left: ${space(1.5)};
${p => p.theme.overflowEllipsis}
line-height: 1.35;
`;
const AlertTitle = styled('div')`
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
`;
const AlertDate = styled('span')`
color: ${p => p.theme[getStatusColor(p)]};
`;
const StyledEmptyStateWarning = styled(EmptyStateWarning)`
height: ${PLACEHOLDER_AND_EMPTY_HEIGHT};
justify-content: center;
`;
export default ProjectLatestAlerts;