organizationCrumb.tsx 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import type {RouteComponentProps} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import sortBy from 'lodash/sortBy';
  4. import IdBadge from 'sentry/components/idBadge';
  5. import OrganizationsStore from 'sentry/stores/organizationsStore';
  6. import {useLegacyStore} from 'sentry/stores/useLegacyStore';
  7. import type {Organization} from 'sentry/types/organization';
  8. import {browserHistory} from 'sentry/utils/browserHistory';
  9. import recreateRoute from 'sentry/utils/recreateRoute';
  10. import {resolveRoute} from 'sentry/utils/resolveRoute';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import BreadcrumbDropdown from './breadcrumbDropdown';
  13. import findFirstRouteWithoutRouteParam from './findFirstRouteWithoutRouteParam';
  14. import MenuItem from './menuItem';
  15. import {CrumbLink} from '.';
  16. type Props = RouteComponentProps<{projectId?: string}, {}>;
  17. function OrganizationCrumb({params, routes, route, ...props}: Props) {
  18. const {organizations} = useLegacyStore(OrganizationsStore);
  19. const organization = useOrganization();
  20. const handleSelect = (item: {value: Organization}) => {
  21. // If we are currently in a project context, and we're attempting to switch organizations,
  22. // then we need to default to index route (e.g. `route`)
  23. //
  24. // Otherwise, find the last route without a router param
  25. // e.g. if you are on API details, we want the API listing
  26. // This fails if our route tree is not nested
  27. const hasProjectParam = !!params.projectId;
  28. let destinationRoute = hasProjectParam
  29. ? route
  30. : findFirstRouteWithoutRouteParam(routes.slice(routes.indexOf(route)));
  31. // It's possible there is no route without route params (e.g. organization settings index),
  32. // in which case, we can use the org settings index route (e.g. `route`)
  33. if (!hasProjectParam && typeof destinationRoute === 'undefined') {
  34. destinationRoute = route;
  35. }
  36. if (destinationRoute === undefined) {
  37. return;
  38. }
  39. const itemOrg = item.value;
  40. const path = recreateRoute(destinationRoute, {
  41. routes,
  42. params: {...params, orgId: itemOrg.slug},
  43. });
  44. const resolvedUrl = resolveRoute(path, organization, itemOrg);
  45. // If we have a shift in domains, we can't use history
  46. if (resolvedUrl.startsWith('http')) {
  47. window.location.assign(resolvedUrl);
  48. } else {
  49. browserHistory.push(resolvedUrl);
  50. }
  51. };
  52. if (!organization) {
  53. return null;
  54. }
  55. const hasMenu = organizations.length > 1;
  56. const orgSettings = `/settings/${organization.slug}/`;
  57. return (
  58. <BreadcrumbDropdown
  59. name={
  60. <CrumbLink to={orgSettings}>
  61. <BadgeWrapper>
  62. <IdBadge avatarSize={18} organization={organization} />
  63. </BadgeWrapper>
  64. </CrumbLink>
  65. }
  66. onSelect={handleSelect}
  67. hasMenu={hasMenu}
  68. route={route}
  69. items={sortBy(organizations, ['name']).map((org, index) => ({
  70. index,
  71. value: org,
  72. label: (
  73. <MenuItem>
  74. <IdBadge organization={org} />
  75. </MenuItem>
  76. ),
  77. }))}
  78. {...props}
  79. />
  80. );
  81. }
  82. const BadgeWrapper = styled('div')`
  83. display: flex;
  84. align-items: center;
  85. `;
  86. export {OrganizationCrumb};