organizationCrumb.tsx 3.2 KB

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