mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
181 lines
5.0 KiB
TypeScript
181 lines
5.0 KiB
TypeScript
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 {IconMail} from 'sentry/icons';
|
|
import {t} from 'sentry/locale';
|
|
import type {Member, Organization} from 'sentry/types';
|
|
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}) =>
|
|
children({
|
|
disabled: !organization.features.includes('invite-members'),
|
|
onTriggerModal,
|
|
}),
|
|
});
|
|
|
|
class OrganizationMembersWrapper extends DeprecatedAsyncView<Props, State> {
|
|
getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
|
|
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<Member>) =>
|
|
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 = (
|
|
<InviteMembersButtonHook
|
|
organization={organization}
|
|
onTriggerModal={() =>
|
|
openInviteMembersModal({
|
|
onClose: () => {
|
|
this.fetchData();
|
|
},
|
|
source: 'members_settings',
|
|
})
|
|
}
|
|
>
|
|
{renderInviteMembersButton}
|
|
</InviteMembersButtonHook>
|
|
);
|
|
|
|
return (
|
|
<Fragment>
|
|
<SettingsPageHeader title="Members" action={action} />
|
|
{children &&
|
|
cloneElement(children, {
|
|
requestList,
|
|
inviteRequests,
|
|
onRemoveInviteRequest: this.removeInviteRequest,
|
|
onUpdateInviteRequest: this.updateInviteRequest,
|
|
onRemoveAccessRequest: this.removeAccessRequest,
|
|
showInviteRequests: this.showInviteRequests,
|
|
})}
|
|
</Fragment>
|
|
);
|
|
}
|
|
}
|
|
|
|
function renderInviteMembersButton({
|
|
disabled,
|
|
onTriggerModal,
|
|
}: {
|
|
onTriggerModal: () => void;
|
|
disabled?: boolean;
|
|
}) {
|
|
const action = (
|
|
<Button
|
|
priority="primary"
|
|
size="sm"
|
|
onClick={onTriggerModal}
|
|
data-test-id="email-invite"
|
|
icon={<IconMail />}
|
|
disabled={disabled}
|
|
>
|
|
{t('Invite Members')}
|
|
</Button>
|
|
);
|
|
|
|
return disabled ? (
|
|
<Hovercard
|
|
body={
|
|
<FeatureDisabled
|
|
featureName={t('Invite Members')}
|
|
features="organizations:invite-members"
|
|
hideHelpToggle
|
|
/>
|
|
}
|
|
>
|
|
{action}
|
|
</Hovercard>
|
|
) : (
|
|
action
|
|
);
|
|
}
|
|
|
|
export default withOrganization(OrganizationMembersWrapper);
|