header.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import isPropValid from '@emotion/is-prop-valid';
  2. import styled from '@emotion/styled';
  3. import Breadcrumbs from 'sentry/components/breadcrumbs';
  4. import Button from 'sentry/components/button';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import IdBadge from 'sentry/components/idBadge';
  7. import * as Layout from 'sentry/components/layouts/thirds';
  8. import PageHeading from 'sentry/components/pageHeading';
  9. import {IconCopy, IconEdit} from 'sentry/icons';
  10. import {t} from 'sentry/locale';
  11. import space from 'sentry/styles/space';
  12. import {Organization, Project} from 'sentry/types';
  13. import {MetricRule} from 'sentry/views/alerts/rules/metric/types';
  14. import {isIssueAlert} from '../../../utils';
  15. type Props = {
  16. hasMetricRuleDetailsError: boolean;
  17. organization: Organization;
  18. project?: Project;
  19. rule?: MetricRule;
  20. };
  21. function DetailsHeader({hasMetricRuleDetailsError, rule, organization, project}: Props) {
  22. const isRuleReady = !!rule && !hasMetricRuleDetailsError;
  23. const ruleTitle = rule && !hasMetricRuleDetailsError ? rule.name : '';
  24. const settingsLink =
  25. rule &&
  26. `/organizations/${organization.slug}/alerts/${
  27. isIssueAlert(rule) ? 'rules' : 'metric-rules'
  28. }/${project?.slug ?? rule?.projects?.[0]}/${rule.id}/`;
  29. const duplicateLink = {
  30. pathname: `/organizations/${organization.slug}/alerts/new/metric/`,
  31. query: {
  32. project: project?.slug,
  33. duplicateRuleId: rule?.id,
  34. createFromDuplicate: true,
  35. referrer: 'metric_rule_details',
  36. },
  37. };
  38. return (
  39. <Layout.Header>
  40. <Layout.HeaderContent>
  41. <Breadcrumbs
  42. crumbs={[
  43. {label: t('Alerts'), to: `/organizations/${organization.slug}/alerts/rules/`},
  44. {label: ruleTitle},
  45. ]}
  46. />
  47. <RuleTitle data-test-id="incident-rule-title" loading={!isRuleReady}>
  48. {project && (
  49. <IdBadge
  50. project={project}
  51. avatarSize={28}
  52. hideName
  53. avatarProps={{hasTooltip: true, tooltip: project.slug}}
  54. />
  55. )}
  56. {ruleTitle}
  57. </RuleTitle>
  58. </Layout.HeaderContent>
  59. <Layout.HeaderActions>
  60. <ButtonBar gap={1}>
  61. <Button size="sm" icon={<IconCopy />} to={duplicateLink}>
  62. {t('Duplicate')}
  63. </Button>
  64. <Button size="sm" icon={<IconEdit />} to={settingsLink}>
  65. {t('Edit Rule')}
  66. </Button>
  67. </ButtonBar>
  68. </Layout.HeaderActions>
  69. </Layout.Header>
  70. );
  71. }
  72. export default DetailsHeader;
  73. const RuleTitle = styled(PageHeading, {
  74. shouldForwardProp: p => typeof p === 'string' && isPropValid(p) && p !== 'loading',
  75. })<{loading: boolean}>`
  76. ${p => p.loading && 'opacity: 0'};
  77. line-height: 40px;
  78. display: grid;
  79. grid-template-columns: max-content 1fr;
  80. grid-column-gap: ${space(1)};
  81. align-items: center;
  82. `;