import {Fragment} from 'react'; import type {RouteComponentProps} from 'react-router'; import {closeModal, openEditOwnershipRules, openModal} from 'sentry/actionCreators/modal'; import Access, {hasEveryAccess} from 'sentry/components/acl/access'; import {Button} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import ErrorBoundary from 'sentry/components/errorBoundary'; import Form from 'sentry/components/forms/form'; import JsonForm from 'sentry/components/forms/jsonForm'; import ExternalLink from 'sentry/components/links/externalLink'; import {IconEdit} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import type {CodeOwner, IssueOwnership, Organization, Project} from 'sentry/types'; import routeTitleGen from 'sentry/utils/routeTitle'; import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; import TextBlock from 'sentry/views/settings/components/text/textBlock'; import PermissionAlert from 'sentry/views/settings/project/permissionAlert'; import AddCodeOwnerModal from 'sentry/views/settings/project/projectOwnership/addCodeOwnerModal'; import {CodeOwnerErrors} from 'sentry/views/settings/project/projectOwnership/codeownerErrors'; import {CodeOwnerFileTable} from 'sentry/views/settings/project/projectOwnership/codeOwnerFileTable'; import {OwnershipRulesTable} from 'sentry/views/settings/project/projectOwnership/ownershipRulesTable'; type Props = { organization: Organization; project: Project; } & RouteComponentProps<{projectId: string}, {}>; type State = { codeowners?: CodeOwner[]; ownership?: null | IssueOwnership; } & DeprecatedAsyncView['state']; class ProjectOwnership extends DeprecatedAsyncView { getOwnershipTitle() { return t('Ownership Rules'); } getTitle() { const {project} = this.props; return routeTitleGen(this.getOwnershipTitle(), project.slug, false); } getEndpoints(): ReturnType { const {organization, project} = this.props; const endpoints: ReturnType = [ ['ownership', `/projects/${organization.slug}/${project.slug}/ownership/`], ]; if (organization.features.includes('integrations-codeowners')) { endpoints.push([ 'codeowners', `/projects/${organization.slug}/${project.slug}/codeowners/`, {query: {expand: ['codeMapping', 'ownershipSyntax']}}, ]); } return endpoints; } handleAddCodeOwner = () => { openModal(modalProps => ( )); }; getPlaceholder() { return `#example usage path:src/example/pipeline/* person@sentry.io #infra module:com.module.name.example #sdks url:http://example.com/settings/* #product tags.sku_class:enterprise #enterprise`; } handleOwnershipSave = (ownership: IssueOwnership) => { this.setState(prevState => ({ ...prevState, ownership, })); closeModal(); }; handleCodeOwnerAdded = (data: CodeOwner) => { const {codeowners} = this.state; const newCodeowners = [data, ...(codeowners || [])]; this.setState({codeowners: newCodeowners}); }; handleCodeOwnerDeleted = (data: CodeOwner) => { const {codeowners} = this.state; const newCodeowners = (codeowners || []).filter( codeowner => codeowner.id !== data.id ); this.setState({codeowners: newCodeowners}); }; handleCodeOwnerUpdated = (data: CodeOwner) => { const codeowners = this.state.codeowners || []; const index = codeowners.findIndex(item => item.id === data.id); this.setState({ codeowners: [...codeowners.slice(0, index), data, ...codeowners.slice(index + 1)], }); }; renderBody() { const {project, organization} = this.props; const {ownership, codeowners} = this.state; const disabled = !hasEveryAccess(['project:write'], {organization, project}); const editOwnershipRulesDisabled = !hasEveryAccess(['project:read'], { organization, project, }); const hasCodeowners = organization.features?.includes('integrations-codeowners'); return ( {hasCodeowners && ( {({hasAccess}) => ( )} )} } /> {tct( `Auto-assign issues to users and teams. To learn more, [link:read the docs].`, { link: ( ), } )} {ownership && ( )} {hasCodeowners && ( )} {ownership && (
)}
); } } export default ProjectOwnership;