index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import styled from '@emotion/styled';
  2. import Footer from 'sentry/components/footer';
  3. import HookOrDefault from 'sentry/components/hookOrDefault';
  4. import Nav from 'sentry/components/nav';
  5. import {NavContextProvider} from 'sentry/components/nav/context';
  6. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  7. import Sidebar from 'sentry/components/sidebar';
  8. import type {Organization} from 'sentry/types/organization';
  9. import useRouteAnalyticsHookSetup from 'sentry/utils/routeAnalytics/useRouteAnalyticsHookSetup';
  10. import useDevToolbar from 'sentry/utils/useDevToolbar';
  11. import {useIsSentryEmployee} from 'sentry/utils/useIsSentryEmployee';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import OrganizationContainer from 'sentry/views/organizationContainer';
  14. import Body from './body';
  15. interface Props {
  16. children: React.ReactNode;
  17. }
  18. const OrganizationHeader = HookOrDefault({
  19. hookName: 'component:organization-header',
  20. });
  21. function DevToolInit() {
  22. const isEmployee = useIsSentryEmployee();
  23. const organization = useOrganization();
  24. const showDevToolbar = organization.features.includes('devtoolbar');
  25. useDevToolbar({enabled: showDevToolbar && isEmployee});
  26. return null;
  27. }
  28. function OrganizationLayout({children}: Props) {
  29. useRouteAnalyticsHookSetup();
  30. // XXX(epurkhiser): The OrganizationContainer is responsible for ensuring the
  31. // oganization is loaded before rendering children. Organization may not be
  32. // loaded yet when this first renders.
  33. const organization = useOrganization({allowNull: true});
  34. const hasNavigationV2 = organization?.features.includes('navigation-sidebar-v2');
  35. const App = hasNavigationV2 ? AppLayout : LegacyAppLayout;
  36. return (
  37. <SentryDocumentTitle noSuffix title={organization?.name ?? 'Sentry'}>
  38. <OrganizationContainer>
  39. <App organization={organization}>{children}</App>
  40. </OrganizationContainer>
  41. </SentryDocumentTitle>
  42. );
  43. }
  44. interface LayoutProps extends Props {
  45. organization: Organization | null;
  46. }
  47. function AppLayout({children, organization}: LayoutProps) {
  48. return (
  49. <NavContextProvider>
  50. <AppContainer className="app">
  51. <Nav />
  52. {/* The `#main` selector is used to make the app content `inert` when an overlay is active */}
  53. <BodyContainer id="main">
  54. {organization && <OrganizationHeader organization={organization} />}
  55. {organization && <DevToolInit />}
  56. <Body>{children}</Body>
  57. <Footer />
  58. </BodyContainer>
  59. </AppContainer>
  60. </NavContextProvider>
  61. );
  62. }
  63. function LegacyAppLayout({children, organization}: LayoutProps) {
  64. return (
  65. <div className="app">
  66. {organization && <OrganizationHeader organization={organization} />}
  67. {organization && <DevToolInit />}
  68. <Sidebar />
  69. <Body>{children}</Body>
  70. <Footer />
  71. </div>
  72. );
  73. }
  74. const AppContainer = styled('div')`
  75. display: flex;
  76. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  77. flex-direction: row;
  78. }
  79. `;
  80. const BodyContainer = styled('div')`
  81. display: flex;
  82. flex-direction: column;
  83. flex: 1;
  84. `;
  85. export default OrganizationLayout;