Skip to content

Commit

Permalink
new modal layout
Browse files Browse the repository at this point in the history
  • Loading branch information
ameliahsu committed Sep 13, 2024
1 parent f31cb0d commit b3e67e0
Show file tree
Hide file tree
Showing 3 changed files with 425 additions and 1 deletion.
26 changes: 25 additions & 1 deletion static/app/components/modals/inviteMembersModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import ErrorBoundary from 'sentry/components/errorBoundary';
import LoadingError from 'sentry/components/loadingError';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import InviteMembersModalNew from 'sentry/components/modals/inviteMembersModal/inviteMembersModalNew';
import InviteMembersModalView from 'sentry/components/modals/inviteMembersModal/inviteMembersModalview';
import type {InviteRow} from 'sentry/components/modals/inviteMembersModal/types';
import useInviteModal from 'sentry/components/modals/inviteMembersModal/useInviteModal';
Expand All @@ -19,6 +20,8 @@ interface InviteMembersModalProps extends ModalRenderProps {
}

function InviteMembersModal({
Header,
Body,
closeModal,
initialData,
source,
Expand Down Expand Up @@ -70,7 +73,28 @@ function InviteMembersModal({
onSendInvites={sendInvites}
>
{({sendInvites: _sendInvites, canSend, headerInfo}) => {
return (
return organization.features.includes('invite-members-new-modal') ? (
<InviteMembersModalNew
canSend={canSend}
Header={Header}
Body={Body}
complete={complete}
Footer={Footer}
headerInfo={headerInfo}
invites={invites}
inviteStatus={inviteStatus}
member={memberResult.data}
pendingInvites={pendingInvites}
reset={reset}
sendingInvites={sendingInvites}
sendInvites={sendInvites}
setEmails={setEmails}
setRole={setRole}
setTeams={setTeams}
willInvite={willInvite}
error={error}
/>
) : (
<InviteMembersModalView
addInviteRow={addInviteRow}
canSend={canSend}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import type {ReactNode} from 'react';
import {Fragment} from 'react';
import styled from '@emotion/styled';

import type {ModalRenderProps} from 'sentry/actionCreators/modal';
import Alert from 'sentry/components/alert';
import ButtonBar from 'sentry/components/buttonBar';
import InviteButton from 'sentry/components/modals/inviteMembersModal/inviteButton';
import InviteRowControl from 'sentry/components/modals/inviteMembersModal/inviteRowControlNew';
import InviteStatusMessage from 'sentry/components/modals/inviteMembersModal/inviteStatusMessage';
import type {
InviteRow,
InviteStatus,
NormalizedInvite,
} from 'sentry/components/modals/inviteMembersModal/types';
import {ORG_ROLES} from 'sentry/constants';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Member} from 'sentry/types/organization';

interface Props {
Body: ModalRenderProps['Body'];
Footer: ModalRenderProps['Footer'];
Header: ModalRenderProps['Header'];
canSend: boolean;
complete: boolean;
headerInfo: ReactNode;
inviteStatus: InviteStatus;
invites: NormalizedInvite[];
member: Member | undefined;
pendingInvites: InviteRow[];
reset: () => void;
sendInvites: () => void;
sendingInvites: boolean;
setEmails: (emails: string[], index: number) => void;
setRole: (role: string, index: number) => void;
setTeams: (teams: string[], index: number) => void;
willInvite: boolean;
error?: string;
}

export default function InviteMembersModalNew({
canSend,
complete,
Header,
Body,
Footer,
headerInfo,
invites,
inviteStatus,
member,
pendingInvites,
reset,
sendingInvites,
sendInvites,
setEmails,
setRole,
setTeams,
willInvite,
error,
}: Props) {
const inviteEmails = invites.map(inv => inv.email);
const hasDuplicateEmails = inviteEmails.length !== new Set(inviteEmails).size;
const isValidInvites = invites.length > 0 && !hasDuplicateEmails;

const errorAlert = error ? (
<Alert type="error" showIcon>
{error}
</Alert>
) : null;

return (
<Fragment>
<Header closeButton>
{errorAlert}
<Heading>{t('Invite New Members')}</Heading>
</Header>
<Body>
{willInvite ? (
<Subtext>
{t('Invite unlimited new members to join your organization.')}
</Subtext>
) : (
<Alert type="warning" showIcon>
{t(
'You can’t invite users directly, but we’ll forward your request to an org owner or manager for approval.'
)}
</Alert>
)}

{headerInfo}

<Rows>
{pendingInvites.map(({emails, role, teams}, i) => (
<StyledInviteRow
key={i}
disabled={false}
emails={[...emails]}
role={role}
teams={[...teams]}
roleOptions={member?.orgRoleList ?? ORG_ROLES}
roleDisabledUnallowed={willInvite}
inviteStatus={inviteStatus}
onRemove={reset}
onChangeEmails={opts => setEmails(opts?.map(v => v.value) ?? [], i)}
onChangeRole={value => setRole(value?.value, i)}
onChangeTeams={opts => setTeams(opts ? opts.map(v => v.value) : [], i)}
/>
))}
</Rows>

<Footer>
<FooterContent>
<div>
<InviteStatusMessage
complete={complete}
hasDuplicateEmails={hasDuplicateEmails}
inviteStatus={inviteStatus}
sendingInvites={sendingInvites}
willInvite={willInvite}
/>
</div>

<ButtonBar gap={1}>
<Fragment>
<InviteButton
invites={invites}
willInvite={willInvite}
size="sm"
data-test-id="send-invites"
priority="primary"
disabled={!canSend || !isValidInvites}
onClick={sendInvites}
/>
</Fragment>
</ButtonBar>
</FooterContent>
</Footer>
</Body>
</Fragment>
);
}

const Heading = styled('h1')`
font-weight: ${p => p.theme.fontWeightNormal};
font-size: ${p => p.theme.headerFontSize};
margin-top: 0;
margin-bottom: ${space(0.75)};
`;

const Subtext = styled('p')`
color: ${p => p.theme.subText};
margin-bottom: ${space(3)};
`;

const Rows = styled('ul')`
list-style: none;
padding: 0;
margin: 0;
`;

const StyledInviteRow = styled(InviteRowControl)`
margin-bottom: ${space(1.5)};
`;

const FooterContent = styled('div')`
display: flex;
gap: ${space(1)};
align-items: center;
justify-content: space-between;
flex: 1;
`;
Loading

0 comments on commit b3e67e0

Please sign in to comment.