projectTeams.tsx 3.2 KB

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