projectTeams.tsx 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  2. import {
  3. useAddTeamToProject,
  4. useFetchProjectTeams,
  5. useRemoveTeamFromProject,
  6. } from 'sentry/actionCreators/projects';
  7. import {hasEveryAccess} from 'sentry/components/acl/access';
  8. import LoadingError from 'sentry/components/loadingError';
  9. import LoadingIndicator from 'sentry/components/loadingIndicator';
  10. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  11. import {t, tct} from 'sentry/locale';
  12. import TeamStore from 'sentry/stores/teamStore';
  13. import type {Organization} from 'sentry/types/organization';
  14. import type {Project} from 'sentry/types/project';
  15. import routeTitleGen from 'sentry/utils/routeTitle';
  16. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  17. import TeamSelectForProject from 'sentry/views/settings/components/teamSelect/teamSelectForProject';
  18. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  19. import PermissionAlert from 'sentry/views/settings/project/permissionAlert';
  20. type ProjectTeamsProps = {
  21. organization: Organization;
  22. project: Project;
  23. };
  24. export function ProjectTeams({organization, project}: ProjectTeamsProps) {
  25. const {
  26. data: projectTeams,
  27. isPending,
  28. isError,
  29. } = useFetchProjectTeams({orgSlug: organization.slug, projectSlug: project.slug});
  30. const handleAddTeamToProject = useAddTeamToProject({
  31. orgSlug: organization.slug,
  32. projectSlug: project.slug,
  33. });
  34. const handleRemoveTeamFromProject = useRemoveTeamFromProject({
  35. orgSlug: organization.slug,
  36. projectSlug: project.slug,
  37. });
  38. const canCreateTeam =
  39. organization.access.includes('org:write') &&
  40. organization.access.includes('team:write') &&
  41. organization.access.includes('project:write');
  42. const hasWriteAccess = hasEveryAccess(['project:write'], {organization, project});
  43. if (isError) {
  44. return <LoadingError message={t('Failed to load project teams')} />;
  45. }
  46. if (isPending) {
  47. return <LoadingIndicator />;
  48. }
  49. return (
  50. <SentryDocumentTitle title={routeTitleGen(t('Project Teams'), project.slug, false)}>
  51. <div>
  52. <SettingsPageHeader title={t('Project Teams for %s', project.slug)} />
  53. <TextBlock>
  54. {t(
  55. 'These teams and their members have access to this project. They can be assigned to issues and alerts created in it.'
  56. )}
  57. </TextBlock>
  58. <TextBlock>
  59. {t(
  60. 'Team Admins can grant other teams access to this project. However, they cannot revoke access unless they are admins for the other teams too.'
  61. )}
  62. </TextBlock>
  63. <PermissionAlert project={project} />
  64. <TeamSelectForProject
  65. disabled={!hasWriteAccess}
  66. canCreateTeam={canCreateTeam}
  67. organization={organization}
  68. project={project}
  69. selectedTeams={projectTeams ?? []}
  70. onAddTeam={teamSlug => {
  71. const team = TeamStore.getBySlug(teamSlug);
  72. if (!team) {
  73. addErrorMessage(tct('Unable to find "[teamSlug]"', {teamSlug}));
  74. return;
  75. }
  76. handleAddTeamToProject(team);
  77. }}
  78. onRemoveTeam={handleRemoveTeamFromProject}
  79. onCreateTeam={handleAddTeamToProject}
  80. />
  81. </div>
  82. </SentryDocumentTitle>
  83. );
  84. }
  85. export default ProjectTeams;