12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037 |
- import {Fragment, useCallback, useContext, useEffect} from 'react';
- import {css} from '@emotion/react';
- import styled from '@emotion/styled';
- import {hideSidebar, showSidebar} from 'sentry/actionCreators/preferences';
- import Feature from 'sentry/components/acl/feature';
- import GuideAnchor from 'sentry/components/assistant/guideAnchor';
- import {Chevron} from 'sentry/components/chevron';
- import FeedbackOnboardingSidebar from 'sentry/components/feedback/feedbackOnboarding/sidebar';
- import Hook from 'sentry/components/hook';
- import {OnboardingContext} from 'sentry/components/onboarding/onboardingContext';
- import {getMergedTasks} from 'sentry/components/onboardingWizard/taskConfig';
- import {hasQuickStartUpdatesFeature} from 'sentry/components/onboardingWizard/utils';
- import PerformanceOnboardingSidebar from 'sentry/components/performanceOnboarding/sidebar';
- import ReplaysOnboardingSidebar from 'sentry/components/replaysOnboarding/sidebar';
- import {
- ExpandedContext,
- ExpandedContextProvider,
- } from 'sentry/components/sidebar/expandedContextProvider';
- import {NewOnboardingStatus} from 'sentry/components/sidebar/newOnboardingStatus';
- import {isDone} from 'sentry/components/sidebar/utils';
- import {
- IconDashboard,
- IconGraph,
- IconIssues,
- IconLightning,
- IconMegaphone,
- IconProject,
- IconReleases,
- IconSearch,
- IconSettings,
- IconSiren,
- IconStats,
- IconSupport,
- IconTimer,
- } from 'sentry/icons';
- import {t} from 'sentry/locale';
- import ConfigStore from 'sentry/stores/configStore';
- import DemoWalkthroughStore from 'sentry/stores/demoWalkthroughStore';
- import HookStore from 'sentry/stores/hookStore';
- import PreferencesStore from 'sentry/stores/preferencesStore';
- import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
- import {useLegacyStore} from 'sentry/stores/useLegacyStore';
- import {space} from 'sentry/styles/space';
- import type {Organization} from 'sentry/types/organization';
- import {isDemoWalkthrough} from 'sentry/utils/demoMode';
- import {getDiscoverLandingUrl} from 'sentry/utils/discover/urls';
- import {isActiveSuperuser} from 'sentry/utils/isActiveSuperuser';
- import {hasCustomMetrics} from 'sentry/utils/metrics/features';
- import theme from 'sentry/utils/theme';
- import normalizeUrl from 'sentry/utils/url/normalizeUrl';
- import {useLocation} from 'sentry/utils/useLocation';
- import useMedia from 'sentry/utils/useMedia';
- import useOrganization from 'sentry/utils/useOrganization';
- import useProjects from 'sentry/utils/useProjects';
- import {useModuleURLBuilder} from 'sentry/views/insights/common/utils/useModuleURL';
- import {MODULE_SIDEBAR_TITLE as HTTP_MODULE_SIDEBAR_TITLE} from 'sentry/views/insights/http/settings';
- import {
- AI_LANDING_SUB_PATH,
- AI_SIDEBAR_LABEL,
- } from 'sentry/views/insights/pages/ai/settings';
- import {
- BACKEND_LANDING_SUB_PATH,
- BACKEND_SIDEBAR_LABEL,
- } from 'sentry/views/insights/pages/backend/settings';
- import {
- FRONTEND_LANDING_SUB_PATH,
- FRONTEND_SIDEBAR_LABEL,
- } from 'sentry/views/insights/pages/frontend/settings';
- import {
- MOBILE_LANDING_SUB_PATH,
- MOBILE_SIDEBAR_LABEL,
- } from 'sentry/views/insights/pages/mobile/settings';
- import {
- DOMAIN_VIEW_BASE_TITLE,
- DOMAIN_VIEW_BASE_URL,
- } from 'sentry/views/insights/pages/settings';
- import {MODULE_TITLES} from 'sentry/views/insights/settings';
- import MetricsOnboardingSidebar from 'sentry/views/metrics/ddmOnboarding/sidebar';
- import {getPerformanceBaseUrl} from 'sentry/views/performance/utils';
- import {ProfilingOnboardingSidebar} from '../profiling/profilingOnboardingSidebar';
- import {Broadcasts} from './broadcasts';
- import SidebarHelp from './help';
- import OnboardingStatus from './onboardingStatus';
- import ServiceIncidents from './serviceIncidents';
- import {SidebarAccordion} from './sidebarAccordion';
- import SidebarDropdown from './sidebarDropdown';
- import SidebarItem from './sidebarItem';
- import type {SidebarOrientation} from './types';
- import {SidebarPanelKey} from './types';
- function activatePanel(panel: SidebarPanelKey) {
- SidebarPanelStore.activatePanel(panel);
- }
- function togglePanel(panel: SidebarPanelKey) {
- SidebarPanelStore.togglePanel(panel);
- }
- function hidePanel(hash?: string) {
- SidebarPanelStore.hidePanel(hash);
- }
- function useOpenOnboardingSidebar(organization?: Organization) {
- const onboardingContext = useContext(OnboardingContext);
- const {projects: project} = useProjects();
- const location = useLocation();
- const openOnboardingSidebar = (() => {
- if (location?.hash === '#welcome') {
- if (organization && !ConfigStore.get('demoMode')) {
- const tasks = getMergedTasks({
- organization,
- projects: project,
- onboardingContext,
- });
- const allDisplayedTasks = tasks.filter(task => task.display);
- const doneTasks = allDisplayedTasks.filter(isDone);
- return !(doneTasks.length >= allDisplayedTasks.length);
- }
- return true;
- }
- return false;
- })();
- useEffect(() => {
- if (openOnboardingSidebar) {
- activatePanel(SidebarPanelKey.ONBOARDING_WIZARD);
- }
- }, [openOnboardingSidebar]);
- }
- function Sidebar() {
- const location = useLocation();
- const preferences = useLegacyStore(PreferencesStore);
- const activePanel = useLegacyStore(SidebarPanelStore);
- const organization = useOrganization({allowNull: true});
- const {shouldAccordionFloat} = useContext(ExpandedContext);
- const hasNewNav = organization?.features.includes('navigation-sidebar-v2');
- const hasOrganization = !!organization;
- const isSelfHostedErrorsOnly = ConfigStore.get('isSelfHostedErrorsOnly');
- const hasPerfDomainViews = organization?.features.includes('insights-domain-view');
- const collapsed = hasNewNav ? true : !!preferences.collapsed;
- const horizontal = useMedia(`(max-width: ${theme.breakpoints.medium})`);
- // Panel determines whether to highlight
- const hasPanel = !!activePanel;
- const orientation: SidebarOrientation = horizontal ? 'top' : 'left';
- const sidebarItemProps = {
- orientation,
- collapsed,
- hasPanel,
- organization,
- hasNewNav,
- };
- // Avoid showing superuser UI on self-hosted instances
- const showSuperuserWarning = () => {
- return isActiveSuperuser() && !ConfigStore.get('isSelfHosted');
- };
- // Avoid showing superuser UI on certain organizations
- const isExcludedOrg = () => {
- return HookStore.get('component:superuser-warning-excluded')[0]?.(organization);
- };
- useOpenOnboardingSidebar();
- const toggleCollapse = useCallback(() => {
- if (collapsed) {
- showSidebar();
- } else {
- hideSidebar();
- }
- }, [collapsed]);
- // Close panel on any navigation
- useEffect(() => void hidePanel(), [location?.pathname]);
- // Add classname to body
- useEffect(() => {
- const bcl = document.body.classList;
- bcl.add('body-sidebar');
- return () => bcl.remove('body-sidebar');
- }, []);
- useEffect(() => {
- Object.values(SidebarPanelKey).forEach(key => {
- if (location?.hash === `#sidebar-${key}`) {
- togglePanel(key);
- }
- });
- }, [location?.hash]);
- // Add sidebar collapse classname to body
- useEffect(() => {
- const bcl = document.body.classList;
- if (collapsed) {
- bcl.add('collapsed');
- } else {
- bcl.remove('collapsed');
- }
- return () => bcl.remove('collapsed');
- }, [collapsed]);
- // Add sidebar hasNewNav classname to body
- useEffect(() => {
- const bcl = document.body.classList;
- if (hasNewNav) {
- bcl.add('hasNewNav');
- } else {
- bcl.remove('hasNewNav');
- }
- return () => bcl.remove('hasNewNav');
- }, [hasNewNav]);
- const sidebarAnchor = isDemoWalkthrough() ? (
- <GuideAnchor target="projects" disabled={!DemoWalkthroughStore.get('sidebar')}>
- {t('Projects')}
- </GuideAnchor>
- ) : (
- <GuideAnchor target="projects">{t('Projects')}</GuideAnchor>
- );
- const projects = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- index
- icon={<IconProject />}
- label={sidebarAnchor}
- to={`/organizations/${organization.slug}/projects/`}
- id="projects"
- />
- );
- const issues = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconIssues />}
- label={<GuideAnchor target="issues">{t('Issues')}</GuideAnchor>}
- to={`/organizations/${organization.slug}/issues/`}
- search="?referrer=sidebar"
- id="issues"
- hasNewNav={hasNewNav}
- />
- );
- const discover2 = hasOrganization && (
- <Feature
- hookName="feature-disabled:discover2-sidebar-item"
- features="discover-basic"
- organization={organization}
- >
- <SidebarItem
- {...sidebarItemProps}
- icon={<SubitemDot collapsed />}
- label={<GuideAnchor target="discover">{t('Discover')}</GuideAnchor>}
- to={getDiscoverLandingUrl(organization)}
- id="discover-v2"
- />
- </Feature>
- );
- const moduleURLBuilder = useModuleURLBuilder(true);
- const queries = hasOrganization && (
- <Feature key="db" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={
- <GuideAnchor target="performance-database">{MODULE_TITLES.db}</GuideAnchor>
- }
- to={`/organizations/${organization.slug}/${moduleURLBuilder('db')}/`}
- id="performance-database"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const requests = hasOrganization && (
- <Feature key="http" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={
- <GuideAnchor target="performance-http">{HTTP_MODULE_SIDEBAR_TITLE}</GuideAnchor>
- }
- to={`/organizations/${organization.slug}/${moduleURLBuilder('http')}/`}
- id="performance-http"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const caches = hasOrganization && (
- <Feature key="cache" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={
- <GuideAnchor target="performance-cache">{MODULE_TITLES.cache}</GuideAnchor>
- }
- to={`/organizations/${organization.slug}/${moduleURLBuilder('cache')}/`}
- id="performance-cache"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const webVitals = hasOrganization && (
- <Feature key="vital" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={
- <GuideAnchor target="performance-webvitals">{MODULE_TITLES.vital}</GuideAnchor>
- }
- to={`/organizations/${organization.slug}/${moduleURLBuilder('vital')}/`}
- id="performance-webvitals"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const queues = hasOrganization && (
- <Feature key="queue" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={
- <GuideAnchor target="performance-queues">{MODULE_TITLES.queue}</GuideAnchor>
- }
- to={`/organizations/${organization.slug}/${moduleURLBuilder('queue')}/`}
- id="performance-queues"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- // the mobile screens module is meant to be as a replacement for screen load, app start, and mobile ui
- // so if mobile screens is enabled, we should not show the other mobile modules
- const hasMobileScreensModule =
- hasOrganization && organization.features.includes('insights-mobile-screens-module');
- const screenLoads = hasOrganization && !hasMobileScreensModule && (
- <Feature
- key="screen_load"
- features="insights-entry-points"
- organization={organization}
- >
- <SidebarItem
- {...sidebarItemProps}
- label={MODULE_TITLES.screen_load}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('screen_load')}/`}
- id="performance-mobile-screens"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const appStarts = hasOrganization && !hasMobileScreensModule && (
- <Feature key="app_start" features="insights-entry-points" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- label={MODULE_TITLES.app_start}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('app_start')}/`}
- id="performance-mobile-app-startup"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const mobileUI = hasOrganization && !hasMobileScreensModule && (
- <Feature
- key="mobile-ui"
- features={['insights-entry-points', 'starfish-mobile-ui-module']}
- organization={organization}
- >
- <SidebarItem
- {...sidebarItemProps}
- label={MODULE_TITLES['mobile-ui']}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('mobile-ui')}/`}
- id="performance-mobile-ui"
- icon={<SubitemDot collapsed />}
- isAlpha
- />
- </Feature>
- );
- const mobileScreens = hasOrganization && hasMobileScreensModule && (
- <Feature
- key="mobile-screens"
- features={['insights-entry-points']}
- organization={organization}
- >
- <SidebarItem
- {...sidebarItemProps}
- label={MODULE_TITLES['mobile-screens']}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('mobile-screens')}/`}
- id="performance-mobile-screens"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const resources = hasOrganization && (
- <Feature key="resource" features="insights-entry-points">
- <SidebarItem
- {...sidebarItemProps}
- label={<GuideAnchor target="starfish">{MODULE_TITLES.resource}</GuideAnchor>}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('resource')}/`}
- id="performance-browser-resources"
- icon={<SubitemDot collapsed />}
- />
- </Feature>
- );
- const traces = hasOrganization && (
- <Feature features="performance-trace-explorer">
- <SidebarItem
- {...sidebarItemProps}
- label={<GuideAnchor target="traces">{t('Traces')}</GuideAnchor>}
- to={`/organizations/${organization.slug}/traces/`}
- id="performance-trace-explorer"
- icon={<SubitemDot collapsed />}
- isBeta
- />
- </Feature>
- );
- const llmMonitoring = hasOrganization && (
- <Feature features={['insights-entry-points']} organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- icon={<SubitemDot collapsed />}
- label={MODULE_TITLES.ai}
- to={`/organizations/${organization.slug}/${moduleURLBuilder('ai')}/`}
- id="llm-monitoring"
- />
- </Feature>
- );
- const performance = hasOrganization && (
- <Feature
- hookName="feature-disabled:performance-sidebar-item"
- features="performance-view"
- organization={organization}
- >
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconLightning />}
- label={
- <GuideAnchor target="performance">
- {hasNewNav ? 'Perf.' : t('Performance')}
- </GuideAnchor>
- }
- to={`${getPerformanceBaseUrl(organization.slug)}/`}
- id="performance"
- />
- </Feature>
- );
- const releases = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconReleases />}
- label={<GuideAnchor target="releases">{t('Releases')}</GuideAnchor>}
- to={`/organizations/${organization.slug}/releases/`}
- id="releases"
- />
- );
- const userFeedback = hasOrganization && (
- <Feature features="old-user-feedback" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconSupport />}
- label={t('User Feedback')}
- to={`/organizations/${organization.slug}/user-feedback/`}
- id="user-feedback"
- />
- </Feature>
- );
- const feedback = hasOrganization && (
- <Feature features="user-feedback-ui" organization={organization}>
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconMegaphone />}
- label={t('User Feedback')}
- variant="short"
- to={`/organizations/${organization.slug}/feedback/`}
- id="feedback"
- />
- </Feature>
- );
- const alerts = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconSiren />}
- label={t('Alerts')}
- to={`/organizations/${organization.slug}/alerts/rules/`}
- id="alerts"
- />
- );
- const monitors = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconTimer />}
- label={t('Crons')}
- to={`/organizations/${organization.slug}/crons/`}
- id="crons"
- />
- );
- const replays = hasOrganization && (
- <Feature
- hookName="feature-disabled:replay-sidebar-item"
- features="session-replay-ui"
- organization={organization}
- requireAll={false}
- >
- <SidebarItem
- {...sidebarItemProps}
- icon={<SubitemDot collapsed />}
- label={t('Replays')}
- to={`/organizations/${organization.slug}/replays/`}
- id="replays"
- />
- </Feature>
- );
- const metricsPath = `/organizations/${organization?.slug}/metrics/`;
- const metrics = hasOrganization && hasCustomMetrics(organization) && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<SubitemDot collapsed />}
- label={t('Metrics')}
- to={metricsPath}
- search={location?.pathname === normalizeUrl(metricsPath) ? location.search : ''}
- id="metrics"
- badgeTitle={t(
- 'The Metrics beta will end and we will retire the current solution on October 7th, 2024'
- )}
- isBeta
- />
- );
- const dashboards = hasOrganization && (
- <Feature
- hookName="feature-disabled:dashboards-sidebar-item"
- features={['discover', 'discover-query', 'dashboards-basic', 'dashboards-edit']}
- organization={organization}
- requireAll={false}
- >
- <SidebarItem
- {...sidebarItemProps}
- index
- icon={<IconDashboard />}
- label={hasNewNav ? 'Dash.' : t('Dashboards')}
- to={`/organizations/${organization.slug}/dashboards/`}
- id="customizable-dashboards"
- />
- </Feature>
- );
- const profiling = hasOrganization && (
- <Feature
- hookName="feature-disabled:profiling-sidebar-item"
- features="profiling"
- organization={organization}
- requireAll={false}
- >
- <SidebarItem
- {...sidebarItemProps}
- index
- icon={<SubitemDot collapsed />}
- label={t('Profiles')}
- to={`/organizations/${organization.slug}/profiling/`}
- id="profiling"
- />
- </Feature>
- );
- const stats = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconStats />}
- label={t('Stats')}
- to={`/organizations/${organization.slug}/stats/`}
- id="stats"
- />
- );
- const settings = hasOrganization && (
- <SidebarItem
- {...sidebarItemProps}
- icon={<IconSettings />}
- label={t('Settings')}
- to={`/settings/${organization.slug}/`}
- id="settings"
- />
- );
- const performanceDomains = hasOrganization && (
- <Feature
- features={['insights-domain-view', 'performance-view']}
- organization={organization}
- >
- <SidebarAccordion
- {...sidebarItemProps}
- icon={<IconGraph />}
- label={DOMAIN_VIEW_BASE_TITLE}
- id="insights-domains"
- initiallyExpanded={false}
- exact={!shouldAccordionFloat}
- >
- <SidebarItem
- {...sidebarItemProps}
- label={FRONTEND_SIDEBAR_LABEL}
- to={`/organizations/${organization.slug}/${DOMAIN_VIEW_BASE_URL}/${FRONTEND_LANDING_SUB_PATH}/`}
- id="performance-domains-web"
- icon={<SubitemDot collapsed />}
- />
- <SidebarItem
- {...sidebarItemProps}
- label={BACKEND_SIDEBAR_LABEL}
- to={`/organizations/${organization.slug}/${DOMAIN_VIEW_BASE_URL}/${BACKEND_LANDING_SUB_PATH}/`}
- id="performance-domains-platform"
- icon={<SubitemDot collapsed />}
- />
- <SidebarItem
- {...sidebarItemProps}
- label={MOBILE_SIDEBAR_LABEL}
- to={`/organizations/${organization.slug}/${DOMAIN_VIEW_BASE_URL}/${MOBILE_LANDING_SUB_PATH}/`}
- id="performance-domains-mobile"
- icon={<SubitemDot collapsed />}
- />
- <SidebarItem
- {...sidebarItemProps}
- label={AI_SIDEBAR_LABEL}
- to={`/organizations/${organization.slug}/${DOMAIN_VIEW_BASE_URL}/${AI_LANDING_SUB_PATH}/`}
- id="performance-domains-ai"
- icon={<SubitemDot collapsed />}
- />
- </SidebarAccordion>
- </Feature>
- );
- const insights = hasOrganization && !hasPerfDomainViews && (
- <Feature key="insights" features="insights-entry-points" organization={organization}>
- <SidebarAccordion
- {...sidebarItemProps}
- icon={<IconGraph />}
- label={<GuideAnchor target="insights">{t('Insights')}</GuideAnchor>}
- id="insights"
- initiallyExpanded={false}
- exact={!shouldAccordionFloat}
- >
- {requests}
- {queries}
- {resources}
- {appStarts}
- {screenLoads}
- {webVitals}
- {caches}
- {queues}
- {mobileUI}
- {mobileScreens}
- {llmMonitoring}
- </SidebarAccordion>
- </Feature>
- );
- // Sidebar accordion includes a secondary list of nav items
- // TODO: replace with a secondary panel
- const explore = (
- <SidebarAccordion
- {...sidebarItemProps}
- icon={<IconSearch />}
- label={<GuideAnchor target="explore">{t('Explore')}</GuideAnchor>}
- id="explore"
- exact={!shouldAccordionFloat}
- >
- {traces}
- {metrics}
- {profiling}
- {replays}
- {discover2}
- </SidebarAccordion>
- );
- return (
- <SidebarWrapper
- aria-label={t('Primary Navigation')}
- collapsed={collapsed}
- hasNewNav={hasNewNav}
- >
- <ExpandedContextProvider>
- <SidebarSectionGroupPrimary>
- <DropdownSidebarSection
- isSuperuser={showSuperuserWarning() && !isExcludedOrg()}
- hasNewNav={hasNewNav}
- >
- <SidebarDropdown
- orientation={orientation}
- collapsed={hasNewNav || collapsed}
- />
- {showSuperuserWarning() && !isExcludedOrg() && (
- <Hook name="component:superuser-warning" organization={organization} />
- )}
- </DropdownSidebarSection>
- <PrimaryItems>
- {hasOrganization && (
- <Fragment>
- <SidebarSection hasNewNav={hasNewNav}>
- {issues}
- {projects}
- </SidebarSection>
- {!isSelfHostedErrorsOnly && (
- <Fragment>
- <SidebarSection hasNewNav={hasNewNav}>
- {explore}
- {insights}
- {performanceDomains}
- </SidebarSection>
- <SidebarSection hasNewNav={hasNewNav}>
- {performance}
- {feedback}
- {monitors}
- {alerts}
- {dashboards}
- {releases}
- </SidebarSection>
- </Fragment>
- )}
- {isSelfHostedErrorsOnly && (
- <Fragment>
- <SidebarSection hasNewNav={hasNewNav}>
- {alerts}
- {discover2}
- {dashboards}
- {releases}
- {userFeedback}
- </SidebarSection>
- </Fragment>
- )}
- <SidebarSection hasNewNav={hasNewNav}>
- {stats}
- {settings}
- </SidebarSection>
- </Fragment>
- )}
- </PrimaryItems>
- </SidebarSectionGroupPrimary>
- {hasOrganization && (
- <SidebarSectionGroup hasNewNav={hasNewNav}>
- {/* What are the onboarding sidebars? */}
- <PerformanceOnboardingSidebar
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.PERFORMANCE_ONBOARDING)}
- hidePanel={() => hidePanel('performance-sidequest')}
- {...sidebarItemProps}
- />
- <FeedbackOnboardingSidebar
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.FEEDBACK_ONBOARDING)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- <ReplaysOnboardingSidebar
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.REPLAYS_ONBOARDING)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- <ProfilingOnboardingSidebar
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.PROFILING_ONBOARDING)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- <MetricsOnboardingSidebar
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.METRICS_ONBOARDING)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- <SidebarSection hasNewNav={hasNewNav} noMargin noPadding>
- {hasQuickStartUpdatesFeature(organization) ? (
- <NewOnboardingStatus
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.ONBOARDING_WIZARD)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- ) : (
- <OnboardingStatus
- org={organization}
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.ONBOARDING_WIZARD)}
- hidePanel={hidePanel}
- {...sidebarItemProps}
- />
- )}
- </SidebarSection>
- <SidebarSection hasNewNav={hasNewNav}>
- {HookStore.get('sidebar:bottom-items').length > 0 &&
- HookStore.get('sidebar:bottom-items')[0]({
- orientation,
- collapsed,
- hasPanel,
- organization,
- })}
- <SidebarHelp
- orientation={orientation}
- collapsed={collapsed}
- hidePanel={hidePanel}
- organization={organization}
- />
- <Broadcasts
- orientation={orientation}
- collapsed={collapsed}
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.BROADCASTS)}
- hidePanel={hidePanel}
- />
- <ServiceIncidents
- orientation={orientation}
- collapsed={collapsed}
- currentPanel={activePanel}
- onShowPanel={() => togglePanel(SidebarPanelKey.SERVICE_INCIDENTS)}
- hidePanel={hidePanel}
- />
- </SidebarSection>
- {!horizontal && !hasNewNav && (
- <SidebarSection hasNewNav={hasNewNav}>
- <SidebarCollapseItem
- id="collapse"
- data-test-id="sidebar-collapse"
- {...sidebarItemProps}
- icon={<Chevron direction={collapsed ? 'right' : 'left'} />}
- label={collapsed ? t('Expand') : t('Collapse')}
- onClick={toggleCollapse}
- />
- </SidebarSection>
- )}
- </SidebarSectionGroup>
- )}
- </ExpandedContextProvider>
- </SidebarWrapper>
- );
- }
- export default Sidebar;
- const responsiveFlex = css`
- display: flex;
- flex-direction: column;
- @media (max-width: ${theme.breakpoints.medium}) {
- flex-direction: row;
- }
- `;
- export const SidebarWrapper = styled('nav')<{collapsed: boolean; hasNewNav?: boolean}>`
- background: ${p => p.theme.sidebarGradient};
- color: ${p => p.theme.sidebar.color};
- line-height: 1;
- padding: 12px 0 2px; /* Allows for 32px avatars */
- width: ${p =>
- p.theme.sidebar[
- p.hasNewNav
- ? 'semiCollapsedWidth'
- : p.collapsed
- ? 'collapsedWidth'
- : 'expandedWidth'
- ]};
- position: fixed;
- top: ${p => (ConfigStore.get('demoMode') ? p.theme.demo.headerSize : 0)};
- left: 0;
- bottom: 0;
- justify-content: space-between;
- z-index: ${p => p.theme.zIndex.sidebar};
- border-right: solid 1px ${p => p.theme.sidebarBorder};
- ${responsiveFlex};
- @media (max-width: ${p => p.theme.breakpoints.medium}) {
- top: 0;
- left: 0;
- right: 0;
- height: ${p => p.theme.sidebar.mobileHeight};
- bottom: auto;
- width: auto;
- padding: 0 ${space(1)};
- align-items: center;
- border-right: none;
- border-bottom: solid 1px ${p => p.theme.sidebarBorder};
- }
- `;
- const SidebarSectionGroup = styled('div')<{hasNewNav?: boolean}>`
- ${responsiveFlex};
- flex-shrink: 0; /* prevents shrinking on Safari */
- gap: 1px;
- ${p => p.hasNewNav && `align-items: center;`}
- `;
- const SidebarSectionGroupPrimary = styled('div')`
- ${responsiveFlex};
- /* necessary for child flexing on msedge and ff */
- min-height: 0;
- min-width: 0;
- flex: 1;
- /* expand to fill the entire height on mobile */
- @media (max-width: ${p => p.theme.breakpoints.medium}) {
- height: 100%;
- align-items: center;
- }
- `;
- const PrimaryItems = styled('div')`
- overflow-y: auto;
- overflow-x: hidden;
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: 1px;
- -ms-overflow-style: -ms-autohiding-scrollbar;
- scrollbar-color: ${p => p.theme.sidebar.scrollbarThumbColor}
- ${p => p.theme.sidebar.scrollbarColorTrack};
- scrollbar-width: ${p => p.theme.sidebar.scrollbarWidth};
- @media (max-height: 675px) and (min-width: ${p => p.theme.breakpoints.medium}) {
- border-bottom: 1px solid ${p => p.theme.sidebarBorder};
- padding-bottom: ${space(1)};
- box-shadow: rgba(0, 0, 0, 0.15) 0px -10px 10px inset;
- }
- @media (max-width: ${p => p.theme.breakpoints.medium}) {
- overflow-y: hidden;
- overflow-x: auto;
- flex-direction: row;
- height: 100%;
- align-items: center;
- border-right: 1px solid ${p => p.theme.sidebarBorder};
- padding-right: ${space(1)};
- margin-right: ${space(0.5)};
- box-shadow: rgba(0, 0, 0, 0.15) -10px 0px 10px inset;
- ::-webkit-scrollbar {
- display: none;
- }
- }
- `;
- const SubitemDot = styled('div')<{collapsed: boolean}>`
- width: 3px;
- height: 3px;
- background: currentcolor;
- border-radius: 50%;
- opacity: ${p => (p.collapsed ? 1 : 0)};
- @media (max-width: ${p => p.theme.breakpoints.medium}) {
- opacity: 1;
- }
- `;
- const SidebarSection = styled(SidebarSectionGroup)<{
- hasNewNav?: boolean;
- noMargin?: boolean;
- noPadding?: boolean;
- }>`
- ${p => !p.noMargin && !p.hasNewNav && `margin: ${space(1)} 0`};
- ${p => !p.noPadding && !p.hasNewNav && `padding: 0 ${space(2)}`};
- @media (max-width: ${p => p.theme.breakpoints.small}) {
- margin: 0;
- padding: 0;
- }
- ${p =>
- p.hasNewNav &&
- css`
- @media (max-width: ${p.theme.breakpoints.medium}) {
- margin: 0;
- padding: 0;
- }
- `}
- &:empty {
- display: none;
- }
- `;
- const DropdownSidebarSection = styled(SidebarSection)<{
- hasNewNav?: boolean;
- isSuperuser?: boolean;
- }>`
- position: relative;
- margin: 0;
- padding: ${space(1)} ${space(2)};
- ${p =>
- p.isSuperuser &&
- css`
- &:before {
- content: '';
- position: absolute;
- inset: 0 ${space(1)};
- border-radius: ${p.theme.borderRadius};
- background: ${p.theme.superuserSidebar};
- }
- `}
- ${p => p.hasNewNav && `align-items: center;`}
- `;
- const SidebarCollapseItem = styled(SidebarItem)`
- @media (max-width: ${p => p.theme.breakpoints.medium}) {
- display: none;
- }
- `;
|