Added some additional handling logic

Icons for group type
If 0 adds do not show add button
Remove checkboxes when dynamic
This commit is contained in:
Nick Brown 2022-08-26 15:45:26 +01:00
parent 4d06a84754
commit 48e7cfc7bb
3 changed files with 46 additions and 18 deletions

View File

@ -110,7 +110,7 @@ export default function AddUser({ Group, Mode, context, onCompleted }: Props): R
<DialogTrigger>
<Button appearance="secondary" disabled={running === rState.Running}>{strings.Close}</Button>
</DialogTrigger>
<Button appearance="primary" disabled={running === rState.Completed || running === rState.Running} onClick={add}>{strings.Add}</Button>
{users.length > 0 && <Button appearance="primary" disabled={running === rState.Completed || running === rState.Running} onClick={add}>{strings.Add}</Button>}
</DialogActions>
</DialogSurface>
</Dialog>

View File

@ -57,7 +57,7 @@ export default function RemoveUser({ Group, Users, Mode, context, onCompleted }:
errors.push(v.statusText);
}
});
if (errors.length > 0) throw errors.join(', ');
if (errors.length > 0) throw errors.join('||');
else setRunning(rState.Completed);
} catch (error) {
handleError(error);
@ -77,14 +77,14 @@ export default function RemoveUser({ Group, Users, Mode, context, onCompleted }:
return (
<Dialog open={open} onOpenChange={(event, data) => setOpen(data.open)}>
<DialogTrigger>
<Button appearance='primary' icon={<PersonDeleteRegular />}>{strings.Remove}</Button>
<Button appearance='primary' icon={<PersonDeleteRegular />} disabled={Users.length === 0}>{strings.Remove}</Button>
</DialogTrigger>
<DialogSurface aria-label="label">
<DialogTitle>{Mode === AddUserMode.Owner ? strings.RemoveDialogTitleOwner : strings.RemoveDialogTitle}{Group.displayName}</DialogTitle>
<DialogBody>
<div className={styles.stack}>
{running !== rState.Error && _error && <Alert intent="error">{_error}</Alert>}
{running === rState.Error && <Alert intent="error" action={{ children: 'Retry', onClick: () => setRunning(rState.Running) }}>{_error}</Alert>}
{running !== rState.Error && _error && <Alert intent="error">{_error.split('||').map((v, i) => (<div key={i}>{v}</div>))}</Alert>}
{running === rState.Error && _error && <Alert intent="error" action={{ children: 'Retry', onClick: () => setRunning(rState.Running) }}>{_error.split('||').map((v, i) => (<div key={i}>{v}</div>))}</Alert>}
{running === rState.Completed && <Alert intent="success">{Mode === AddUserMode.Owner ? strings.Owners : strings.Members} {strings.Removed} {Group.displayName}</Alert>}
{Users.length > 0 && <div className={styles.stackHoz} style={{ flexWrap: 'wrap' }}>
{Users.map(u => <div key={u.id} className={styles.stackHoz} style={{ maxWidth: 200, whiteSpace: 'nowrap' }}>

View File

@ -4,21 +4,28 @@ import { IGroupMembershipManagerProps } from './IGroupMembershipManagerProps';
import * as strings from 'GroupMembershipManagerWebPartStrings';
import { MSGraphClientV3 } from '@microsoft/sp-http';
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
import { Checkbox, CheckboxOnChangeData, Spinner, Subtitle1, Textarea } from '@fluentui/react-components';
import { Checkbox, CheckboxOnChangeData, Spinner, Subtitle1, Textarea, Tooltip } from '@fluentui/react-components';
import { Dropdown, Option, Toolbar } from "@fluentui/react-components/unstable";
import { TableBody, TableCell, TableRow, Table } from '@fluentui/react-table';
import AddUser, { AddUserMode } from './AddUser';
import RemoveUser from './DeleteUser';
import { GraphError } from '@microsoft/microsoft-graph-client';
import SPFxPeopleCard from './SPFxPeopleCard';
import { PersonaSize } from 'office-ui-fabric-react/lib/Persona';
import { PeopleTeam24Regular, MailLink24Regular,ShieldLock24Regular, Eye24Regular, EyeOff24Regular } from '@fluentui/react-icons'
type OnSelectData = {
optionValue: string;
selectedOptions: string[];
};
interface IGroup extends MicrosoftGraph.Group {
resourceProvisioningOptions?: string[]
}
export default function GroupMembershipManager(props: IGroupMembershipManagerProps): React.ReactElement<IGroupMembershipManagerProps> {
const { hasTeamsContext, context } = props;
const [groups, setGroups] = React.useState<MicrosoftGraph.Group[]>(null);
const [groups, setGroups] = React.useState<IGroup[]>(null);
const [group, setGroup] = React.useState<string>(null);
const [members, setMembers] = React.useState<MicrosoftGraph.User[]>(null);
const [owners, setOwners] = React.useState<MicrosoftGraph.User[]>([]);
@ -27,7 +34,7 @@ export default function GroupMembershipManager(props: IGroupMembershipManagerPro
React.useEffect(() => {
context.msGraphClientFactory.getClient("3").then((client: MSGraphClientV3) => {
client.api('/me/ownedObjects').select("id,displayName,groupTypes,visibility,securityEnabled,description").get((error, response: { value: MicrosoftGraph.Group[] }) => {
client.api('/me/ownedObjects').select("id,displayName,groupTypes,visibility,mailEnabled,resourceProvisioningOptions,securityEnabled,description").get((error, response: { value: MicrosoftGraph.Group[] }) => {
if (error) throw error;
setGroups(response.value.sort((a, b) => a.displayName.localeCompare(b.displayName)));
}).catch(console.error);
@ -40,11 +47,11 @@ export default function GroupMembershipManager(props: IGroupMembershipManagerPro
setToRemove([]);
setRemoveOwner([]);
const client: MSGraphClientV3 = await context.msGraphClientFactory.getClient("3");
client.api(`/groups/${groups.filter(g => g.displayName === group)[0].id}/members`).select("id,displayName").get((error: GraphError, response: { value: MicrosoftGraph.User[] }) => {
client.api(`/groups/${groups.filter(g => g.displayName === group)[0].id}/members`).select("id,displayName,mail,userPrincipalName").get((error: GraphError, response: { value: MicrosoftGraph.User[] }) => {
if (error) throw error;
setMembers(response.value.sort((a, b) => a.displayName.localeCompare(b.displayName)));
}).catch(console.error);
client.api(`/groups/${groups.filter(g => g.displayName === group)[0].id}/owners`).select("id,displayName").get((error: GraphError, response: { value: MicrosoftGraph.User[] }) => {
client.api(`/groups/${groups.filter(g => g.displayName === group)[0].id}/owners`).select("id,displayName,mail,userPrincipalName").get((error: GraphError, response: { value: MicrosoftGraph.User[] }) => {
if (error) throw error;
setOwners(response.value);
}).catch(console.error);
@ -67,13 +74,30 @@ export default function GroupMembershipManager(props: IGroupMembershipManagerPro
{!groups && <Spinner labelPosition='below' label={strings.LoadingGroups} />}
{groups && <section>
<Subtitle1 block>{strings.PickGroup}</Subtitle1>
<Dropdown placeholder={strings.PickGroup} size="large" onSelect={(e, d?: OnSelectData) => setGroup(d ? d.optionValue : null)}>
{groups.map(group => <Option key={group.id}>{group.displayName}</Option>)}
</Dropdown>
<div className={styles.stackHoz} style={{ alignItems: 'center'}}>
<Dropdown placeholder={strings.PickGroup} size="large" onSelect={(e, d?: OnSelectData) => setGroup(d ? d.optionValue : null)}>
{groups.map(group => <Option id={group.id} key={group.id}>{group.displayName}</Option>)}
</Dropdown>
{group && groups.filter(g => g.displayName === group)[0].resourceProvisioningOptions?.filter(g => g === "Team").length === 1 &&
<Tooltip relationship='label' content="Team"><span><PeopleTeam24Regular /></span></Tooltip>
}
{group && groups.filter(g => g.displayName === group)[0].mailEnabled &&
<Tooltip relationship='label' content="Mail Enabled"><span><MailLink24Regular /></span></Tooltip>
}
{group && groups.filter(g => g.displayName === group)[0].securityEnabled &&
<Tooltip relationship='label' content="Security Enabled"><span><ShieldLock24Regular /></span></Tooltip>
}
{group && groups.filter(g => g.displayName === group)[0].visibility &&
<Tooltip relationship='label' content={groups.filter(g => g.displayName === group)[0].visibility}><span>
{groups.filter(g => g.displayName === group)[0].visibility === 'Public' && <Eye24Regular />}
{groups.filter(g => g.displayName === group)[0].visibility === 'Private' && <EyeOff24Regular />}
</span></Tooltip>
}
</div>
</section>}
{(!members || !owners) && groups && group && <Spinner labelPosition='below' label={strings.LoadingMembers} />}
{groups && group && <div className={styles.stack} style={{marginTop: 10 }}>
<Textarea defaultValue={groups.filter(g => g.displayName === group)[0].description} resize='vertical' readOnly />
<Textarea value={groups.filter(g => g.displayName === group)[0].description} resize='vertical' readOnly />
<div className={`${styles.stackHoz} ${styles.spaceBetween}`} style={{marginTop: 10 }}>
<div style={{width: '49%'}}>
<Subtitle1>{strings.Members}</Subtitle1>
@ -89,7 +113,9 @@ export default function GroupMembershipManager(props: IGroupMembershipManagerPro
{members.map(user => (
<TableRow key={user.id}>
<TableCell>
<Checkbox label={user.displayName} onChange={(ev, data) => checkUser(data, user)} />
{!(groups.filter(g => g.displayName === group)[0].groupTypes?.filter(g => g === "DynamicMembership").length > 0) &&
<Checkbox onChange={(ev, data) => checkUser(data, user)} />}
<SPFxPeopleCard primaryText={user.displayName} serviceScope={context.serviceScope} email={user.userPrincipalName} size={PersonaSize.size24} secondaryText={user.mail} />
</TableCell>
</TableRow>
))}
@ -110,8 +136,10 @@ export default function GroupMembershipManager(props: IGroupMembershipManagerPro
{owners.map(user => (
<TableRow key={user.id}>
<TableCell>
<Checkbox label={user.displayName} onChange={(ev, data) => checkOwner(data, user)} />
</TableCell>
{!(groups.filter(g => g.displayName === group)[0].groupTypes?.filter(g => g === "DynamicMembership").length > 0) &&
<Checkbox onChange={(ev, data) => checkOwner(data, user)} />}
<SPFxPeopleCard primaryText={user.displayName} serviceScope={context.serviceScope} email={user.userPrincipalName} size={PersonaSize.size24} secondaryText={user.mail} />
</TableCell>
</TableRow>
))}
</TableBody>