Skip to content

Commit

Permalink
feat(organizations): Add button and modal for inviting new org member…
Browse files Browse the repository at this point in the history
…s TASK-1373 (#5460)

### 📣 Summary
<!-- Delete this section if changes are internal only. -->
<!-- One sentence summary for the public changelog, worded for
non-technical seasoned Kobo users. -->

Adds UI to invite members to an MMO.

### 💭 Notes
<!-- Delete this section if empty. -->
<!-- Anything else useful that's not said above,worded for
reviewers, testers, and future git archaeologist collegues. Examples:
- screenshots, copy-pasted logs, etc.
- what was tried but didn't work,
- conscious short-term vs long-term tradeoffs,
- proactively answer likely questions,
-->

Just the UI itself, currently does not send or receive any requests.
Made with mantine components.

### 👀 Preview steps
<!-- Delete this section if behavior can't change. -->
<!-- If behavior changes or merely may change, add a preview of a
minimal happy path. -->

Bug template:
1. Have a user in an MMO and has either a Owner or Admin role
2. Enable the feature flag with `ff_inviteOrgMembers=true`
3. See the new section
4. Click on Invite members and see the new modal

---------

Co-authored-by: James Kiger <[email protected]>
  • Loading branch information
duvld and jamesrkiger authored Feb 3, 2025
1 parent c33f9a8 commit 12ecd57
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 5 deletions.
33 changes: 33 additions & 0 deletions jsapp/js/account/organization/InviteModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {Group, Modal, Stack, Text, TextInput, ModalProps} from '@mantine/core';
import ButtonNew from 'jsapp/js/components/common/ButtonNew';
import {Select} from 'jsapp/js/components/common/Select';

export default function InviteModal(props: ModalProps) {
return (
<Modal
opened={props.opened}
onClose={props.onClose}
title={t('Invite memebrs to your team')}
>
<Stack>
<Text>
{t('Enter the username or email address of the person you wish to invite to your team. They will receive an invitation in their inbox.')}
</Text>
<Group w='100%' gap='xs'>
<TextInput
flex={3}
placeholder={t('Enter username or email address')}
/>
<Select
flex={2}
placeholder={'Role'}
data={['Owner', 'Admin', 'Member']}
/>
</Group>
<Group w='100%' justify='flex-end'>
<ButtonNew size='lg'>{t('Send invite')}</ButtonNew>
</Group>
</Stack>
</Modal>
);
}
41 changes: 37 additions & 4 deletions jsapp/js/account/organization/MembersRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,36 @@ import Avatar from 'js/components/common/avatar';
import Badge from 'jsapp/js/components/common/badge';
import MemberActionsDropdown from './MemberActionsDropdown';
import MemberRoleSelector from './MemberRoleSelector';
import ButtonNew from 'jsapp/js/components/common/ButtonNew';
import {Divider, Group, Stack, Text, Title, Box} from '@mantine/core';
import InviteModal from 'js/account/organization/InviteModal';

// Stores, hooks and utilities
import {formatTime} from 'js/utils';
import {OrganizationUserRole, useOrganizationQuery} from './organizationQuery';
import useOrganizationMembersQuery from './membersQuery';
import {useDisclosure} from '@mantine/hooks';

// Constants and types
import type {OrganizationMember} from './membersQuery';
import type {UniversalTableColumn} from 'jsapp/js/universalTable/universalTable.component';

// Styles
import styles from './membersRoute.module.scss';
import {FeatureFlag, useFeatureFlag} from 'jsapp/js/featureFlags';

export default function MembersRoute() {
const orgQuery = useOrganizationQuery();
const [opened, {open, close}] = useDisclosure(false);

if (!orgQuery.data) {
return (
<LoadingSpinner />
);
return <LoadingSpinner />;
}

const isInviteOrgMembersEnabled = useFeatureFlag(
FeatureFlag.orgMemberInvitesEnabled
);

const columns: Array<UniversalTableColumn<OrganizationMember>> = [
{
key: 'user__extra_details__name',
Expand Down Expand Up @@ -63,7 +71,8 @@ export default function MembersRoute() {
key: 'date_joined',
label: t('Date added'),
size: 140,
cellFormatter: (member: OrganizationMember) => formatTime(member.date_joined),
cellFormatter: (member: OrganizationMember) =>
formatTime(member.date_joined),
},
{
key: 'role',
Expand Down Expand Up @@ -141,6 +150,30 @@ export default function MembersRoute() {
<h2 className={styles.headerText}>{t('Members')}</h2>
</header>

{isInviteOrgMembersEnabled &&
!(orgQuery.data.request_user_role === 'member') && (
<Box>
<Divider />
<Group w='100%' justify='space-between'>
<Stack gap='xs' pt='xs' pb='xs'>
{/*TODO: 'Roboto' font is not loading correctly. The styling matches the figma but still looks off.*/}
<Title fw={600} order={5}>{t('Invite members')}</Title>
<Text>
{t('Invite more people to join your team or change their role permissions below.')}
</Text>
</Stack>

<Box>
<ButtonNew size='lg' onClick={open}>
{t('Invite members')}
</ButtonNew>
<InviteModal opened={opened} onClose={close} />
</Box>
</Group>
<Divider mb='md' />
</Box>
)}

<PaginatedQueryUniversalTable<OrganizationMember>
queryHook={useOrganizationMembersQuery}
columns={columns}
Expand Down
3 changes: 2 additions & 1 deletion jsapp/js/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* For our sanity, use camel case and match key with value.
*/
export enum FeatureFlag {
exampleFeatureEnabled = 'exampleFeatureEnabled', //Comment out when we have active FFs
//exampleFeatureEnabled = 'exampleFeatureEnabled', //Comment out when we have active FFs
orgMemberInvitesEnabled = 'orgMemberInvitesEnabled',
}

/**
Expand Down
7 changes: 7 additions & 0 deletions jsapp/js/theme/kobo/Divider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {Divider} from '@mantine/core';

export const DividerThemeKobo = Divider.extend({
defaultProps: {
color: 'gray.7',
},
});
2 changes: 2 additions & 0 deletions jsapp/js/theme/kobo/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const SelectThemeKobo = Select.extend({
comboboxProps: {
offset: 0,
dropdownPadding: 0,
// needed in order to display correctly in a modal
zIndex: 5000,
},
},
});
2 changes: 2 additions & 0 deletions jsapp/js/theme/kobo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {AlertThemeKobo} from './Alert';
import {SelectThemeKobo} from './Select';
import {LoaderThemeKobo} from './Loader';
import {ModalThemeKobo} from './Modal';
import {DividerThemeKobo} from './Divider';

export const themeKobo = createTheme({
primaryColor: 'blue',
Expand Down Expand Up @@ -114,5 +115,6 @@ export const themeKobo = createTheme({
Select: SelectThemeKobo,
Tooltip: TooltipThemeKobo,
Table: TableThemeKobo,
Divider: DividerThemeKobo,
},
});

0 comments on commit 12ecd57

Please sign in to comment.