import { computeNodeActionSet } from '~/views-components/context-menu/action-sets/compute-node-action-set';
import { apiClientAuthorizationActionSet } from '~/views-components/context-menu/action-sets/api-client-authorization-action-set';
import { groupActionSet } from '~/views-components/context-menu/action-sets/group-action-set';
+import { groupMemberActionSet } from '~/views-components/context-menu/action-sets/group-member-action-set';
console.log(`Starting arvados [${getBuildInfo()}]`);
addMenuActionSet(ContextMenuKind.NODE, computeNodeActionSet);
addMenuActionSet(ContextMenuKind.API_CLIENT_AUTHORIZATION, apiClientAuthorizationActionSet);
addMenuActionSet(ContextMenuKind.GROUPS, groupActionSet);
+addMenuActionSet(ContextMenuKind.GROUP_MEMBER, groupMemberActionSet);
fetchConfig()
.then(({ config, apiHost }) => {
import { GroupResource } from '~/models/group';
import { getCommonResourceServiceError, CommonResourceServiceError } from '~/services/common-service/common-resource-service';
import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { PermissionLevel } from '~/models/permission';
+import { PermissionLevel, PermissionResource } from '~/models/permission';
import { PermissionService } from '~/services/permission-service/permission-service';
export const GROUPS_PANEL_ID = "groupsPanel";
export const CREATE_GROUP_USERS_FIELD_NAME = 'users';
export const GROUP_ATTRIBUTES_DIALOG = 'groupAttributesDialog';
export const GROUP_REMOVE_DIALOG = 'groupRemoveDialog';
+export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog';
+export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog';
export const GroupsPanelActions = bindDataExplorerActions(GROUPS_PANEL_ID);
dispatch(dialogActions.OPEN_DIALOG({ id: GROUP_ATTRIBUTES_DIALOG, data }));
};
+export const openGroupMemberAttributes = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const { resources } = getState();
+ const data = getResource<PermissionResource>(uuid)(resources);
+ dispatch(dialogActions.OPEN_DIALOG({ id: MEMBER_ATTRIBUTES_DIALOG, data }));
+ };
+
export const removeGroup = (uuid: string) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
}));
};
+export const removeGroupMember = (uuid: string) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
+ await services.permissionService.delete(uuid);
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+ dispatch<any>(loadGroupsPanel());
+ };
+
+export const openRemoveGroupMemberDialog = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(dialogActions.OPEN_DIALOG({
+ id: MEMBER_REMOVE_DIALOG,
+ data: {
+ title: 'Remove member',
+ text: 'Are you sure you want to remove this member from this group?',
+ confirmButtonLabel: 'Remove',
+ uuid
+ }
+ }));
+ };
+
export interface CreateGroupFormData {
[CREATE_GROUP_NAME_FIELD_NAME]: string;
[CREATE_GROUP_USERS_FIELD_NAME]?: Person[];
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
+import { AdvancedIcon, RemoveIcon, AttributesIcon } from "~/components/icon/icon";
+import { openAdvancedTabDialog } from "~/store/advanced-tab/advanced-tab";
+import { openGroupMemberAttributes, openRemoveGroupMemberDialog } from "~/store/groups-panel/groups-panel-actions";
+
+export const groupMemberActionSet: ContextMenuActionSet = [[{
+ name: "Attributes",
+ icon: AttributesIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(openGroupMemberAttributes(uuid));
+ }
+}, {
+ name: "Advanced",
+ icon: AdvancedIcon,
+ execute: (dispatch, resource) => {
+ dispatch<any>(openAdvancedTabDialog(resource.uuid));
+ }
+}, {
+ name: "Remove",
+ icon: RemoveIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(openRemoveGroupMemberDialog(uuid));
+ }
+}]];
\ No newline at end of file
KEEP_SERVICE = "KeepService",
USER = "User",
NODE = "Node",
- GROUPS = "Group"
+ GROUPS = "Group",
+ GROUP_MEMBER = "GroupMember"
}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, Grid } from "@material-ui/core";
+import { WithDialogProps } from "~/store/dialog/with-dialog";
+import { withDialog } from '~/store/dialog/with-dialog';
+import { WithStyles, withStyles } from '@material-ui/core/styles';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { compose } from "redux";
+import { PermissionResource } from "~/models/permission";
+import { MEMBER_ATTRIBUTES_DIALOG } from "~/store/groups-panel/groups-panel-actions";
+import { UserResource } from "~/models/user";
+
+type CssRules = 'rightContainer' | 'leftContainer' | 'spacing';
+
+const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
+ rightContainer: {
+ textAlign: 'right',
+ paddingRight: theme.spacing.unit * 2,
+ color: theme.palette.grey["500"]
+ },
+ leftContainer: {
+ textAlign: 'left',
+ paddingLeft: theme.spacing.unit * 2
+ },
+ spacing: {
+ paddingTop: theme.spacing.unit * 2
+ },
+}));
+
+interface GroupAttributesDataProps {
+ data: PermissionResource;
+}
+
+type GroupAttributesProps = GroupAttributesDataProps & WithStyles<CssRules>;
+
+export const GroupMemberAttributesDialog = compose(
+ withDialog(MEMBER_ATTRIBUTES_DIALOG),
+ styles)(
+ (props: WithDialogProps<GroupAttributesProps> & GroupAttributesProps) =>
+ <Dialog open={props.open}
+ onClose={props.closeDialog}
+ fullWidth
+ maxWidth="sm">
+ <DialogTitle>Attributes</DialogTitle>
+ <DialogContent>
+ <Typography variant="body2" className={props.classes.spacing}>
+ {props.data && attributes(props.data, props.classes)}
+ </Typography>
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ onClick={props.closeDialog}>
+ Close
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
+
+const attributes = (memberGroup: PermissionResource, classes: any) => {
+ const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name, etag, href, linkClass } = memberGroup;
+ return (
+ <span>
+ <Grid container direction="row">
+ <Grid item xs={5} className={classes.rightContainer}>
+ {name && <Grid item>Name</Grid>}
+ {ownerUuid && <Grid item>Owner uuid</Grid>}
+ {createdAt && <Grid item>Created at</Grid>}
+ {modifiedAt && <Grid item>Modified at</Grid>}
+ {modifiedByUserUuid && <Grid item>Modified by user uuid</Grid>}
+ {modifiedByClientUuid && <Grid item>Modified by client uuid</Grid>}
+ {uuid && <Grid item>uuid</Grid>}
+ {linkClass && <Grid item>Link Class</Grid>}
+ {etag && <Grid item>Etag</Grid>}
+ {href && <Grid item>Href</Grid>}
+ </Grid>
+ <Grid item xs={7} className={classes.leftContainer}>
+ <Grid item>{name}</Grid>
+ <Grid item>{ownerUuid}</Grid>
+ <Grid item>{createdAt}</Grid>
+ <Grid item>{modifiedAt}</Grid>
+ <Grid item>{modifiedByUserUuid}</Grid>
+ <Grid item>{modifiedByClientUuid}</Grid>
+ <Grid item>{uuid}</Grid>
+ <Grid item>{linkClass}</Grid>
+ <Grid item>{etag}</Grid>
+ <Grid item>{href}</Grid>
+ </Grid>
+ </Grid>
+ </span>
+ );
+};
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch, compose } from 'redux';
+import { connect } from "react-redux";
+import { ConfirmationDialog } from "~/components/confirmation-dialog/confirmation-dialog";
+import { withDialog, WithDialogProps } from "~/store/dialog/with-dialog";
+import { removeGroupMember, MEMBER_REMOVE_DIALOG } from '~/store/groups-panel/groups-panel-actions';
+
+const mapDispatchToProps = (dispatch: Dispatch, props: WithDialogProps<any>) => ({
+ onConfirm: () => {
+ props.closeDialog();
+ dispatch<any>(removeGroupMember(props.data.uuid));
+ }
+});
+
+export const RemoveGroupMemberDialog = compose(
+ withDialog(MEMBER_REMOVE_DIALOG),
+ connect(null, mapDispatchToProps)
+)(ConfirmationDialog);
\ No newline at end of file
import { noop } from 'lodash/fp';
import { RootState } from '~/store/store';
import { GROUP_DETAILS_PANEL_ID } from '~/store/group-details-panel/group-details-panel-actions';
+import { openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { ResourcesState, getResource } from '~/store/resources/resources';
+import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+import { PermissionResource } from '~/models/permission';
export enum GroupDetailsPanelColumnNames {
FIRST_NAME = "First name",
};
};
-const mapDispatchToProps = {};
+const mapDispatchToProps = {
+ onContextMenu: openContextMenu,
+};
-export interface GroupDetailsPanelProps { }
+export interface GroupDetailsPanelProps {
+ onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
+ resources: ResourcesState;
+}
export const GroupDetailsPanel = connect(
mapStateToProps, mapDispatchToProps
id={GROUP_DETAILS_PANEL_ID}
onRowClick={noop}
onRowDoubleClick={noop}
- onContextMenu={noop}
+ onContextMenu={this.handleContextMenu}
contextMenuColumn={true}
hideColumnSelector />
);
}
+
+ handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
+ const resource = getResource<PermissionResource>(resourceUuid)(this.props.resources);
+ if (resource) {
+ this.props.onContextMenu(event, {
+ name: '',
+ uuid: resource.uuid,
+ ownerUuid: resource.ownerUuid,
+ kind: resource.kind,
+ menuKind: ContextMenuKind.GROUP_MEMBER
+ });
+ }
+ }
});
import { RemoveGroupDialog } from '~/views-components/groups-dialog/remove-dialog';
import { GroupAttributesDialog } from '~/views-components/groups-dialog/attributes-dialog';
import { GroupDetailsPanel } from '~/views/group-details-panel/group-details-panel';
+import { RemoveGroupMemberDialog } from '~/views-components/groups-dialog/member-remove-dialog';
+import { GroupMemberAttributesDialog } from '~/views-components/groups-dialog/member-attributes-dialog';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<FileRemoveDialog />
<FilesUploadCollectionDialog />
<GroupAttributesDialog />
+ <GroupMemberAttributesDialog />
<HelpApiClientAuthorizationDialog />
<MoveCollectionDialog />
<MoveProcessDialog />
<RemoveApiClientAuthorizationDialog />
<RemoveComputeNodeDialog />
<RemoveGroupDialog />
+ <RemoveGroupMemberDialog />
<RemoveKeepServiceDialog />
<RemoveProcessDialog />
<RemoveRepositoryDialog />