import {cloneElement, Fragment} from 'react'; import type {RouteComponentProps} from 'react-router'; import {openInviteMembersModal} from 'sentry/actionCreators/modal'; import FeatureDisabled from 'sentry/components/acl/featureDisabled'; import {Button} from 'sentry/components/button'; import HookOrDefault from 'sentry/components/hookOrDefault'; import {Hovercard} from 'sentry/components/hovercard'; import {Tooltip} from 'sentry/components/tooltip'; import {IconMail} from 'sentry/icons'; import {t} from 'sentry/locale'; import type {Member, Organization} from 'sentry/types/organization'; import routeTitleGen from 'sentry/utils/routeTitle'; import withOrganization from 'sentry/utils/withOrganization'; import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; type Props = { organization: Organization; children?: any; } & RouteComponentProps<{}, {}>; type State = DeprecatedAsyncView['state'] & { inviteRequests: Member[]; }; const InviteMembersButtonHook = HookOrDefault({ hookName: 'member-invite-button:customization', defaultComponent: ({children, organization, onTriggerModal}) => { const isSsoRequired = organization.requiresSso; const disabled = isSsoRequired || !organization.features.includes('invite-members'); return children({disabled, isSsoRequired, onTriggerModal}); }, }); class OrganizationMembersWrapper extends DeprecatedAsyncView { getEndpoints(): ReturnType { const {organization} = this.props; return [ ['inviteRequests', `/organizations/${organization.slug}/invite-requests/`], ['requestList', `/organizations/${organization.slug}/access-requests/`], ]; } getTitle() { const {organization} = this.props; return routeTitleGen(t('Members'), organization.slug, false); } get onRequestsTab() { return location.pathname.includes('/requests/'); } get hasWriteAccess() { const {organization} = this.props; if (!organization || !organization.access) { return false; } return organization.access.includes('member:write'); } get showInviteRequests() { return this.hasWriteAccess; } get showNavTabs() { const {requestList} = this.state; // show the requests tab if there are pending team requests, // or if the user has access to approve or deny invite requests return (requestList && requestList.length > 0) || this.showInviteRequests; } get requestCount() { const {requestList, inviteRequests} = this.state; let count = requestList.length; // if the user can't see the invite requests panel, // exclude those requests from the total count if (this.showInviteRequests) { count += inviteRequests.length; } return count ? count.toString() : null; } removeAccessRequest = (id: string) => this.setState(state => ({ requestList: state.requestList.filter(request => request.id !== id), })); removeInviteRequest = (id: string) => this.setState(state => ({ inviteRequests: state.inviteRequests.filter(request => request.id !== id), })); updateInviteRequest = (id: string, data: Partial) => this.setState(state => { const inviteRequests = [...state.inviteRequests]; const inviteIndex = inviteRequests.findIndex(request => request.id === id); inviteRequests[inviteIndex] = {...inviteRequests[inviteIndex], ...data}; return {inviteRequests}; }); renderBody() { const {children, organization} = this.props; const {requestList, inviteRequests} = this.state; const action = ( openInviteMembersModal({ onClose: () => { this.fetchData(); }, source: 'members_settings', }) } > {renderInviteMembersButton} ); return ( {children && cloneElement(children, { requestList, inviteRequests, onRemoveInviteRequest: this.removeInviteRequest, onUpdateInviteRequest: this.updateInviteRequest, onRemoveAccessRequest: this.removeAccessRequest, showInviteRequests: this.showInviteRequests, })} ); } } function renderInviteMembersButton({ disabled, isSsoRequired, onTriggerModal, }: { onTriggerModal: () => void; disabled?: boolean; isSsoRequired?: boolean; }) { const action = ( ); return disabled ? ( isSsoRequired ? ( {action} ) : ( } > {action} ) ) : ( action ); } export default withOrganization(OrganizationMembersWrapper);