Files
2024-02-15 17:01:50 +00:00

153 lines
4.4 KiB
TypeScript

import {Component} from 'react';
import styled from '@emotion/styled';
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import type {Client} from 'sentry/api';
import {Button} from 'sentry/components/button';
import Panel from 'sentry/components/panels/panel';
import PanelBody from 'sentry/components/panels/panelBody';
import PanelHeader from 'sentry/components/panels/panelHeader';
import PanelItem from 'sentry/components/panels/panelItem';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {AccessRequest} from 'sentry/types';
import withApi from 'sentry/utils/withApi';
type Props = {
api: Client;
onRemoveAccessRequest: (id: string, isApproved: boolean) => void;
orgSlug: string;
requestList: AccessRequest[];
};
type State = {
accessRequestBusy: Record<string, boolean>;
};
type HandleOpts = {
errorMessage: string;
id: string;
isApproved: boolean;
successMessage: string;
};
class OrganizationAccessRequests extends Component<Props, State> {
state: State = {
accessRequestBusy: {},
};
async handleAction({id, isApproved, successMessage, errorMessage}: HandleOpts) {
const {api, orgSlug, onRemoveAccessRequest} = this.props;
this.setState(state => ({
accessRequestBusy: {...state.accessRequestBusy, [id]: true},
}));
try {
await api.requestPromise(`/organizations/${orgSlug}/access-requests/${id}/`, {
method: 'PUT',
data: {isApproved},
});
onRemoveAccessRequest(id, isApproved);
addSuccessMessage(successMessage);
} catch {
addErrorMessage(errorMessage);
}
this.setState(state => ({
accessRequestBusy: {...state.accessRequestBusy, [id]: false},
}));
}
handleApprove = (id: string, e: React.MouseEvent) => {
e.stopPropagation();
this.handleAction({
id,
isApproved: true,
successMessage: t('Team request approved'),
errorMessage: t('Error approving team request'),
});
};
handleDeny = (id: string, e: React.MouseEvent) => {
e.stopPropagation();
this.handleAction({
id,
isApproved: false,
successMessage: t('Team request denied'),
errorMessage: t('Error denying team request'),
});
};
render() {
const {requestList} = this.props;
const {accessRequestBusy} = this.state;
if (!requestList || !requestList.length) {
return null;
}
return (
<Panel>
<PanelHeader>{t('Pending Team Requests')}</PanelHeader>
<PanelBody>
{requestList.map(({id, member, team, requester}) => {
const memberName =
member.user &&
(member.user.name || member.user.email || member.user.username);
const requesterName =
requester && (requester.name || requester.email || requester.username);
return (
<StyledPanelItem key={id}>
<div data-test-id="request-message">
{requesterName
? tct('[requesterName] requests to add [name] to the [team] team.', {
requesterName,
name: <strong>{memberName}</strong>,
team: <strong>#{team.slug}</strong>,
})
: tct('[name] requests access to the [team] team.', {
name: <strong>{memberName}</strong>,
team: <strong>#{team.slug}</strong>,
})}
</div>
<div>
<StyledButton
priority="primary"
size="sm"
onClick={e => this.handleApprove(id, e)}
busy={accessRequestBusy[id]}
>
{t('Approve')}
</StyledButton>
<Button
busy={accessRequestBusy[id]}
onClick={e => this.handleDeny(id, e)}
size="sm"
>
{t('Deny')}
</Button>
</div>
</StyledPanelItem>
);
})}
</PanelBody>
</Panel>
);
}
}
const StyledPanelItem = styled(PanelItem)`
display: grid;
grid-template-columns: auto max-content;
gap: ${space(2)};
align-items: center;
`;
const StyledButton = styled(Button)`
margin-right: ${space(1)};
`;
export default withApi(OrganizationAccessRequests);