import {Fragment} from 'react';
import styled from '@emotion/styled';
import AlertBadge from 'sentry/components/alertBadge';
import {OnDemandWarningIcon} from 'sentry/components/alerts/onDemandMetricAlert';
import ActorAvatar from 'sentry/components/avatar/actorAvatar';
import {SectionHeading} from 'sentry/components/charts/styles';
import DateTime from 'sentry/components/dateTime';
import Duration from 'sentry/components/duration';
import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
import TimeSince from 'sentry/components/timeSince';
import {IconDiamond} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Actor} from 'sentry/types';
import getDynamicText from 'sentry/utils/getDynamicText';
import {getSearchFilters, isOnDemandSearchKey} from 'sentry/utils/onDemandMetrics/index';
import {capitalize} from 'sentry/utils/string/capitalize';
import {COMPARISON_DELTA_OPTIONS} from 'sentry/views/alerts/rules/metric/constants';
import type {Action, MetricRule} from 'sentry/views/alerts/rules/metric/types';
import {
AlertRuleThresholdType,
AlertRuleTriggerType,
} from 'sentry/views/alerts/rules/metric/types';
import {IncidentStatus} from 'sentry/views/alerts/types';
import {AlertWizardAlertNames} from 'sentry/views/alerts/wizard/options';
import {getAlertTypeFromAggregateDataset} from 'sentry/views/alerts/wizard/utils';
interface MetricDetailsSidebarProps {
rule: MetricRule;
showOnDemandMetricAlertUI: boolean;
}
function TriggerDescription({
rule,
actions,
label,
threshold,
}: {
actions: Action[];
label: string;
rule: MetricRule;
threshold: number;
}) {
const status =
label === AlertRuleTriggerType.CRITICAL
? t('Critical')
: label === AlertRuleTriggerType.WARNING
? t('Warning')
: t('Resolved');
const statusIconColor =
label === AlertRuleTriggerType.CRITICAL
? 'errorText'
: label === AlertRuleTriggerType.WARNING
? 'warningText'
: 'successText';
const defaultAction = t('Change alert status to %s', status);
const aboveThreshold =
label === AlertRuleTriggerType.RESOLVE
? rule.thresholdType === AlertRuleThresholdType.BELOW
: rule.thresholdType === AlertRuleThresholdType.ABOVE;
const thresholdTypeText = aboveThreshold
? rule.comparisonDelta
? t('higher')
: t('above')
: rule.comparisonDelta
? t('lower')
: t('below');
const timeWindow = ;
const metricName = capitalize(
AlertWizardAlertNames[getAlertTypeFromAggregateDataset(rule)]
);
const thresholdText = rule.comparisonDelta
? tct(
'[metric] is [threshold]% [comparisonType] in [timeWindow] compared to the [comparisonDelta]',
{
metric: metricName,
threshold,
comparisonType: thresholdTypeText,
timeWindow,
comparisonDelta: (
COMPARISON_DELTA_OPTIONS.find(({value}) => value === rule.comparisonDelta) ??
COMPARISON_DELTA_OPTIONS[0]
).label,
}
)
: tct('[metric] is [condition] in [timeWindow]', {
metric: metricName,
condition: `${thresholdTypeText} ${threshold}`,
timeWindow,
});
return (
{t('%s Conditions', status)}
{t('When')}
{thresholdText}
{t('Then')}
{actions.map(action => (
{action.desc}
))}
{defaultAction}
);
}
export function MetricDetailsSidebar({
rule,
showOnDemandMetricAlertUI,
}: MetricDetailsSidebarProps) {
// get current status
const latestIncident = rule.latestIncident;
const status = latestIncident ? latestIncident.status : IncidentStatus.CLOSED;
// The date at which the alert was triggered or resolved
const activityDate = latestIncident?.dateClosed ?? latestIncident?.dateStarted ?? null;
const criticalTrigger = rule.triggers.find(
({label}) => label === AlertRuleTriggerType.CRITICAL
);
const warningTrigger = rule.triggers.find(
({label}) => label === AlertRuleTriggerType.WARNING
);
const ownerId = rule.owner?.split(':')[1];
const teamActor = ownerId && {type: 'team' as Actor['type'], id: ownerId, name: ''};
return (
{t('Alert Status')}
{t('Last Triggered')}
{activityDate ? : t('No alerts triggered')}
{typeof criticalTrigger?.alertThreshold === 'number' && (
)}
{typeof warningTrigger?.alertThreshold === 'number' && (
)}
{typeof rule.resolveThreshold === 'number' && (
)}
{showOnDemandMetricAlertUI && (
{t('Filters Used')}
{getSearchFilters(rule.query).map(({key, operator, value}) => (
))}
)}
{t('Alert Rule Details')}
{rule.environment ?? '-'}}
/>
}
/>
{rule.createdBy && (
{rule.createdBy.name ?? '-'}
}
/>
)}
{rule.dateModified && (
}
/>
)}
: t('Unassigned')
}
/>
);
}
function FilterKeyValueTableRow({
keyName,
operator,
value,
}: {
keyName: string;
operator: string;
value: string;
}) {
return (
{isOnDemandSearchKey(keyName) && (
)}
{keyName}
}
value={
{operator} {value}
}
/>
);
}
const KeyWrapper = styled('div')`
display: flex;
gap: ${space(0.75)};
> span {
margin-top: ${space(0.25)};
height: ${space(2)};
}
`;
const SidebarGroup = styled('div')`
margin-bottom: ${space(3)};
`;
const HeaderItem = styled('div')`
flex: 1;
display: flex;
flex-direction: column;
> *:nth-child(2) {
flex: 1;
display: flex;
align-items: center;
}
`;
const Status = styled('div')`
position: relative;
display: grid;
grid-template-columns: auto auto auto;
gap: ${space(0.5)};
font-size: ${p => p.theme.fontSizeLarge};
`;
const StatusContainer = styled('div')`
height: 60px;
display: flex;
margin-bottom: ${space(1)};
`;
const Heading = styled(SectionHeading)<{noMargin?: boolean}>`
margin-top: ${p => (p.noMargin ? 0 : space(2))};
margin-bottom: ${p => (p.noMargin ? 0 : space(1))};
`;
const OverflowTableValue = styled('div')`
${p => p.theme.overflowEllipsis}
`;
const TriggerContainer = styled('div')`
display: grid;
grid-template-rows: auto auto auto;
gap: ${space(1)};
margin-top: ${space(4)};
`;
const TriggerTitle = styled('div')`
display: grid;
grid-template-columns: 20px 1fr;
align-items: center;
`;
const TriggerTitleText = styled('h4')`
color: ${p => p.theme.subText};
font-size: ${p => p.theme.fontSizeMedium};
margin: 0;
line-height: 24px;
min-width: 40px;
`;
const TriggerStep = styled('div')`
display: grid;
grid-template-columns: 40px 1fr;
align-items: stretch;
`;
const TriggerActions = styled('div')`
display: grid;
grid-template-columns: repeat(1fr);
gap: ${space(0.25)};
align-items: center;
`;
const TriggerText = styled('span')`
display: block;
background-color: ${p => p.theme.surface200};
padding: ${space(0.25)} ${space(0.75)};
border-radius: ${p => p.theme.borderRadius};
color: ${p => p.theme.textColor};
font-size: ${p => p.theme.fontSizeSmall};
width: 100%;
font-weight: 400;
`;