domainViewHeader.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {LocationDescriptor} from 'history';
  4. import {Breadcrumbs, type Crumb} from 'sentry/components/breadcrumbs';
  5. import ButtonBar from 'sentry/components/buttonBar';
  6. import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
  7. import * as Layout from 'sentry/components/layouts/thirds';
  8. import {TabList, Tabs} from 'sentry/components/tabs';
  9. import {IconBusiness} from 'sentry/icons';
  10. import {space} from 'sentry/styles/space';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import {useModuleTitles} from 'sentry/views/insights/common/utils/useModuleTitle';
  13. import {
  14. type RoutableModuleNames,
  15. useModuleURLBuilder,
  16. } from 'sentry/views/insights/common/utils/useModuleURL';
  17. import {
  18. DOMAIN_VIEW_BASE_TITLE,
  19. OVERVIEW_PAGE_TITLE,
  20. } from 'sentry/views/insights/pages/settings';
  21. import {isModuleEnabled} from 'sentry/views/insights/pages/utils';
  22. import type {ModuleName} from 'sentry/views/insights/types';
  23. export type Props = {
  24. domainBaseUrl: string;
  25. domainTitle: string;
  26. headerTitle: React.ReactNode;
  27. modules: ModuleName[];
  28. selectedModule: ModuleName | undefined;
  29. additionalBreadCrumbs?: Crumb[];
  30. additonalHeaderActions?: React.ReactNode;
  31. hideDefaultTabs?: boolean;
  32. tabs?: {onTabChange: (key: string) => void; tabList: React.ReactNode; value: string};
  33. };
  34. type Tab = {
  35. key: string;
  36. label: React.ReactNode;
  37. to: LocationDescriptor;
  38. };
  39. export function DomainViewHeader({
  40. modules,
  41. headerTitle,
  42. domainTitle,
  43. selectedModule,
  44. hideDefaultTabs,
  45. additonalHeaderActions,
  46. additionalBreadCrumbs = [],
  47. domainBaseUrl,
  48. tabs,
  49. }: Props) {
  50. const organization = useOrganization();
  51. const moduleURLBuilder = useModuleURLBuilder();
  52. const moduleTitles = useModuleTitles();
  53. const baseCrumbs: Crumb[] = [
  54. {
  55. label: DOMAIN_VIEW_BASE_TITLE,
  56. to: undefined, // There is no base /performance/ page
  57. preservePageFilters: true,
  58. },
  59. {
  60. label: domainTitle,
  61. to: domainBaseUrl,
  62. preservePageFilters: true,
  63. },
  64. {
  65. label: selectedModule ? moduleTitles[selectedModule] : OVERVIEW_PAGE_TITLE,
  66. to: selectedModule
  67. ? `${moduleURLBuilder(selectedModule as RoutableModuleNames)}/`
  68. : domainBaseUrl,
  69. preservePageFilters: true,
  70. },
  71. ...additionalBreadCrumbs,
  72. ];
  73. const showModuleTabs = organization.features.includes('insights-entry-points');
  74. const tabValue =
  75. hideDefaultTabs && tabs?.value ? tabs.value : selectedModule ?? OVERVIEW_PAGE_TITLE;
  76. const tabList: Tab[] = [
  77. {
  78. key: OVERVIEW_PAGE_TITLE,
  79. label: OVERVIEW_PAGE_TITLE,
  80. to: domainBaseUrl,
  81. },
  82. ];
  83. if (showModuleTabs) {
  84. tabList.push(
  85. ...modules.map(moduleName => ({
  86. key: moduleName,
  87. label: <TabLabel moduleName={moduleName} />,
  88. to: `${moduleURLBuilder(moduleName as RoutableModuleNames)}/`,
  89. }))
  90. );
  91. }
  92. return (
  93. <Fragment>
  94. <Layout.Header>
  95. <Layout.HeaderContent>
  96. <Breadcrumbs crumbs={baseCrumbs} />
  97. <Layout.Title>{headerTitle}</Layout.Title>
  98. </Layout.HeaderContent>
  99. <Layout.HeaderActions>
  100. <ButtonBar gap={1}>
  101. {additonalHeaderActions}
  102. <FeedbackWidgetButton />
  103. </ButtonBar>
  104. </Layout.HeaderActions>
  105. <Tabs value={tabValue} onChange={tabs?.onTabChange}>
  106. {!hideDefaultTabs && (
  107. <TabList hideBorder>
  108. {tabList.map(tab => (
  109. <TabList.Item key={tab.key} to={tab.to}>
  110. {tab.label}
  111. </TabList.Item>
  112. ))}
  113. </TabList>
  114. )}
  115. {hideDefaultTabs && tabs && tabs.tabList}
  116. </Tabs>
  117. </Layout.Header>
  118. </Fragment>
  119. );
  120. }
  121. function TabLabel({moduleName}: {moduleName: ModuleName}) {
  122. const moduleTitles = useModuleTitles();
  123. const organization = useOrganization();
  124. const showBusinessIcon = !isModuleEnabled(moduleName, organization);
  125. if (showBusinessIcon) {
  126. return (
  127. <TabWithIconContainer>
  128. {moduleTitles[moduleName]}
  129. <IconBusiness />
  130. </TabWithIconContainer>
  131. );
  132. }
  133. return <Fragment>{moduleTitles[moduleName]}</Fragment>;
  134. }
  135. const TabWithIconContainer = styled('div')`
  136. display: inline-flex;
  137. align-items: center;
  138. text-align: left;
  139. gap: ${space(0.5)};
  140. `;