[ARVADOS-WORKBENCH2] created: 2.3.0-39-g207429c4
Git user
git at public.arvados.org
Fri Nov 19 19:52:35 UTC 2021
at 207429c4a8863bce7e53082179e3e3b78c67b073 (commit)
commit 207429c4a8863bce7e53082179e3e3b78c67b073
Author: Stephen Smith <stephen at curii.com>
Date: Tue Nov 16 21:10:28 2021 -0500
18123: Hide add member button unless the user can_manage the group.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index 7b06a693..bade28cb 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -11,13 +11,15 @@ import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, Reso
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
-import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog } from 'store/group-details-panel/group-details-panel-actions';
+import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog, getCurrentGroupDetailsPanelUuid } 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';
import { Grid, Button, Tabs, Tab, Paper } from '@material-ui/core';
import { AddIcon } from 'components/icon/icon';
+import { getUserUuid } from 'common/getuser';
+import { GroupResource } from 'models/group';
export enum GroupDetailsPanelMembersColumnNames {
FULL_NAME = "Name",
@@ -120,8 +122,13 @@ export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
];
const mapStateToProps = (state: RootState) => {
+ const groupUuid = getCurrentGroupDetailsPanelUuid(state.properties);
+ const group = getResource<GroupResource>(groupUuid || '')(state.resources);
+ const userUuid = getUserUuid(state);
+
return {
- resources: state.resources
+ resources: state.resources,
+ groupCanManage: userUuid ? group?.writableBy?.includes(userUuid) : false,
};
};
@@ -134,6 +141,7 @@ export interface GroupDetailsPanelProps {
onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
onAddUser: () => void;
resources: ResourcesState;
+ groupCanManage: boolean;
}
export const GroupDetailsPanel = connect(
@@ -166,14 +174,15 @@ export const GroupDetailsPanel = connect(
hideColumnSelector
hideSearchInput
actions={
- <Grid container justify='flex-end'>
- <Button
+ this.props.groupCanManage &&
+ <Grid container justify='flex-end'>
+ <Button
variant="contained"
color="primary"
onClick={this.props.onAddUser}>
<AddIcon /> Add user
- </Button>
- </Grid>
+ </Button>
+ </Grid>
}
paperProps={{
elevation: 0,
commit 134cf300692c9f09f1a79d02295e1d6b7242f32d
Author: Stephen Smith <stephen at curii.com>
Date: Thu Nov 11 11:23:28 2021 -0500
18123: Add isActive checkbox to group member list for user members
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 73ef32b0..aa200942 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -185,16 +185,30 @@ export const ResourceEmail = connect(
return resource || { email: '' };
})(renderEmail);
-const renderIsActive = (props: { uuid: string, isActive: boolean, toggleIsActive: (uuid: string) => void }) =>
- <Checkbox
- color="primary"
- checked={props.isActive}
- onClick={() => props.toggleIsActive(props.uuid)} />;
+const renderIsActive = (props: { uuid: string, kind: ResourceKind, isActive: boolean, toggleIsActive: (uuid: string) => void }) => {
+ if (props.kind === ResourceKind.USER) {
+ return <Checkbox
+ color="primary"
+ checked={props.isActive}
+ onClick={() => props.toggleIsActive(props.uuid)} />;
+ } else {
+ return <Typography />;
+ }
+}
export const ResourceIsActive = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<UserResource>(props.uuid)(state.resources);
- return resource || { isActive: false };
+ return resource || { isActive: false, kind: ResourceKind.NONE };
+ }, { toggleIsActive }
+)(renderIsActive);
+
+export const ResourceLinkTailIsActive = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const tailResource = getResource<UserResource>(link?.tailUuid || '')(state.resources);
+
+ return tailResource || { isActive: false, kind: ResourceKind.NONE };
}, { toggleIsActive }
)(renderIsActive);
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index b4a8c6d3..7b06a693 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, ResourceLinkTailUsername, ResourceLinkHeadPermissionLevel, ResourceLinkTailPermissionLevel, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete } from 'views-components/data-explorer/renderers';
+import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, ResourceLinkTailUsername, ResourceLinkHeadPermissionLevel, ResourceLinkTailPermissionLevel, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete, ResourceLinkTailIsActive } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
@@ -21,10 +21,11 @@ import { AddIcon } from 'components/icon/icon';
export enum GroupDetailsPanelMembersColumnNames {
FULL_NAME = "Name",
- UUID = "UUID",
- EMAIL = "Email",
USERNAME = "Username",
+ EMAIL = "Email",
+ ACTIVE = "User Active",
PERMISSION = "Permission",
+ UUID = "UUID",
REMOVE = "Remove",
}
@@ -57,6 +58,13 @@ export const groupDetailsMembersPanelColumns: DataColumns<string> = [
filters: createTree(),
render: uuid => <ResourceLinkTailEmail uuid={uuid} />
},
+ {
+ name: GroupDetailsPanelMembersColumnNames.ACTIVE,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkTailIsActive uuid={uuid} />
+ },
{
name: GroupDetailsPanelMembersColumnNames.PERMISSION,
selected: true,
commit 844241adac4afa32679f07874e5c659896399fdc
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 10 22:55:05 2021 -0500
18123: stopSubmit on failure in edit group dialog
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index e961b347..6d17db19 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -85,6 +85,7 @@ export const updateGroup = (project: ProjectUpdateFormDialogData) =>
dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_UPDATE_FORM_NAME }));
return updatedGroup;
} catch (e) {
+ dispatch(stopSubmit(PROJECT_UPDATE_FORM_NAME));
const error = getCommonResourceServiceError(e);
if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) {
dispatch(stopSubmit(PROJECT_UPDATE_FORM_NAME, { name: 'Group with the same name already exists.' } as FormErrors));
commit 022c93cff94f9f253e1df177ad75dde0dde2597f
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 10 20:34:19 2021 -0500
18123: Add group edit dialog actions to re-use edit project dialog.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index 1c4a0732..e961b347 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from 'redux';
-import { reset, startSubmit, stopSubmit, FormErrors } from 'redux-form';
+import { reset, startSubmit, stopSubmit, FormErrors, initialize } from 'redux-form';
import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action";
import { dialogActions } from 'store/dialog/dialog-actions';
import { Participant } from 'views-components/sharing-dialog/participant-select';
@@ -16,6 +16,7 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { PermissionLevel } from 'models/permission';
import { PermissionService } from 'services/permission-service/permission-service';
import { FilterBuilder } from 'services/api/filter-builder';
+import { ProjectUpdateFormDialogData, PROJECT_UPDATE_FORM_NAME } from 'store/projects/project-update-actions';
export const GROUPS_PANEL_ID = "groupsPanel";
@@ -66,6 +67,32 @@ export const openRemoveGroupDialog = (uuid: string) =>
}));
};
+// Group edit dialog uses project update dialog with sourcePanel set to reload the appropriate parts
+export const openGroupUpdateDialog = (resource: ProjectUpdateFormDialogData) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
+ dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {sourcePanel: GroupClass.ROLE} }));
+ };
+
+export const updateGroup = (project: ProjectUpdateFormDialogData) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const uuid = project.uuid || '';
+ dispatch(startSubmit(PROJECT_UPDATE_FORM_NAME));
+ try {
+ const updatedGroup = await services.groupsService.update(uuid, { name: project.name, description: project.description });
+ dispatch(GroupsPanelActions.REQUEST_ITEMS());
+ dispatch(reset(PROJECT_UPDATE_FORM_NAME));
+ dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_UPDATE_FORM_NAME }));
+ return updatedGroup;
+ } catch (e) {
+ const error = getCommonResourceServiceError(e);
+ if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) {
+ dispatch(stopSubmit(PROJECT_UPDATE_FORM_NAME, { name: 'Group with the same name already exists.' } as FormErrors));
+ }
+ return ;
+ }
+ };
+
export interface CreateGroupFormData {
[CREATE_GROUP_NAME_FIELD_NAME]: string;
[CREATE_GROUP_USERS_FIELD_NAME]?: Participant[];
diff --git a/src/store/projects/project-update-actions.ts b/src/store/projects/project-update-actions.ts
index 35100eb6..45065b62 100644
--- a/src/store/projects/project-update-actions.ts
+++ b/src/store/projects/project-update-actions.ts
@@ -3,12 +3,13 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from "redux";
-import { FormErrors, initialize, startSubmit, stopSubmit } from 'redux-form';
+import { FormErrors, initialize, reset, startSubmit, stopSubmit } from 'redux-form';
import { RootState } from "store/store";
import { dialogActions } from "store/dialog/dialog-actions";
import { getCommonResourceServiceError, CommonResourceServiceError } from "services/common-service/common-resource-service";
import { ServiceRepository } from "services/services";
import { projectPanelActions } from 'store/project-panel/project-panel-action';
+import { GroupClass } from "models/group";
export interface ProjectUpdateFormDialogData {
uuid: string;
@@ -21,7 +22,7 @@ export const PROJECT_UPDATE_FORM_NAME = 'projectUpdateFormName';
export const openProjectUpdateDialog = (resource: ProjectUpdateFormDialogData) =>
(dispatch: Dispatch, getState: () => RootState) => {
dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
- dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {} }));
+ dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {sourcePanel: GroupClass.PROJECT} }));
};
export const updateProject = (project: ProjectUpdateFormDialogData) =>
@@ -31,6 +32,7 @@ export const updateProject = (project: ProjectUpdateFormDialogData) =>
try {
const updatedProject = await services.projectService.update(uuid, { name: project.name, description: project.description });
dispatch(projectPanelActions.REQUEST_ITEMS());
+ dispatch(reset(PROJECT_UPDATE_FORM_NAME));
dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_UPDATE_FORM_NAME }));
return updatedProject;
} catch (e) {
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index 9c89d199..527d9d74 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -272,6 +272,20 @@ export const updateProject = (data: projectUpdateActions.ProjectUpdateFormDialog
}
};
+export const updateGroup = (data: projectUpdateActions.ProjectUpdateFormDialogData) =>
+ async (dispatch: Dispatch) => {
+ const updatedGroup = await dispatch<any>(groupPanelActions.updateGroup(data));
+ if (updatedGroup) {
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: "Group has been successfully updated.",
+ hideDuration: 2000,
+ kind: SnackbarKind.SUCCESS
+ }));
+ await dispatch<any>(loadSidePanelTreeProjects(updatedGroup.ownerUuid));
+ dispatch<any>(reloadProjectMatchingUuid([updatedGroup.ownerUuid, updatedGroup.uuid]));
+ }
+ };
+
export const loadCollection = (uuid: string) =>
handleFirstTimeLoad(
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
diff --git a/src/views-components/context-menu/action-sets/group-action-set.ts b/src/views-components/context-menu/action-sets/group-action-set.ts
index f2c9b92f..874a601b 100644
--- a/src/views-components/context-menu/action-sets/group-action-set.ts
+++ b/src/views-components/context-menu/action-sets/group-action-set.ts
@@ -5,14 +5,13 @@
import { ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set";
import { RenameIcon, AdvancedIcon, RemoveIcon, AttributesIcon } from "components/icon/icon";
import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab";
-import { openGroupAttributes, openRemoveGroupDialog } from "store/groups-panel/groups-panel-actions";
-import { openProjectUpdateDialog } from "store/projects/project-update-actions";
+import { openGroupAttributes, openRemoveGroupDialog, openGroupUpdateDialog } from "store/groups-panel/groups-panel-actions";
export const groupActionSet: ContextMenuActionSet = [[{
name: "Rename",
icon: RenameIcon,
execute: (dispatch, resource) => {
- dispatch<any>(openProjectUpdateDialog(resource));
+ dispatch<any>(openGroupUpdateDialog(resource));
}
}, {
name: "Attributes",
diff --git a/src/views-components/dialog-forms/update-project-dialog.ts b/src/views-components/dialog-forms/update-project-dialog.ts
index dca51b96..119e9256 100644
--- a/src/views-components/dialog-forms/update-project-dialog.ts
+++ b/src/views-components/dialog-forms/update-project-dialog.ts
@@ -7,14 +7,25 @@ import { reduxForm } from 'redux-form';
import { withDialog } from "store/dialog/with-dialog";
import { DialogProjectUpdate } from 'views-components/dialog-update/dialog-project-update';
import { PROJECT_UPDATE_FORM_NAME, ProjectUpdateFormDialogData } from 'store/projects/project-update-actions';
-import { updateProject } from 'store/workbench/workbench-actions';
+import { updateProject, updateGroup } from 'store/workbench/workbench-actions';
+import { GroupClass } from "models/group";
export const UpdateProjectDialog = compose(
withDialog(PROJECT_UPDATE_FORM_NAME),
reduxForm<ProjectUpdateFormDialogData>({
form: PROJECT_UPDATE_FORM_NAME,
- onSubmit: (data, dispatch) => {
- dispatch(updateProject(data));
+ onSubmit: (data, dispatch, props) => {
+ console.log(props);
+ switch (props.data.sourcePanel) {
+ case GroupClass.PROJECT:
+ dispatch(updateProject(data));
+ break;
+ case GroupClass.ROLE:
+ dispatch(updateGroup(data));
+ break;
+ default:
+ break;
+ }
}
})
-)(DialogProjectUpdate);
\ No newline at end of file
+)(DialogProjectUpdate);
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index a9f59f68..4f25f6e5 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -101,8 +101,9 @@ export const GroupsPanel = connect(
const resource = getResource<GroupResource>(resourceUuid)(this.props.resources);
if (resource) {
this.props.onContextMenu(event, {
- name: '',
+ name: resource.name,
uuid: resource.uuid,
+ description: resource.description,
ownerUuid: resource.ownerUuid,
kind: resource.kind,
menuKind: ContextMenuKind.GROUPS
commit e02fcdadca54f1dc970734f7c5ce0bc6407e10c6
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 10 11:01:12 2021 -0500
18123: Use project update dialog for renaming group
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index 9c9f15cf..1c4a0732 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from 'redux';
-import { reset, initialize, startSubmit, stopSubmit, FormErrors } from 'redux-form';
+import { reset, startSubmit, stopSubmit, FormErrors } from 'redux-form';
import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action";
import { dialogActions } from 'store/dialog/dialog-actions';
import { Participant } from 'views-components/sharing-dialog/participant-select';
@@ -25,12 +25,6 @@ export const CREATE_GROUP_FORM = "createGroupForm";
export const CREATE_GROUP_NAME_FIELD_NAME = 'name';
export const CREATE_GROUP_USERS_FIELD_NAME = 'users';
-// Rename group dialog
-export const RENAME_GROUP_DIALOG = "renameGroupDialog";
-export const RENAME_GROUP_FORM = "renameGroupForm";
-export const RENAME_GROUP_UUID_FIELD_NAME = 'uuid';
-export const RENAME_GROUP_NAME_FIELD_NAME = 'name';
-
export const GROUP_ATTRIBUTES_DIALOG = 'groupAttributesDialog';
export const GROUP_REMOVE_DIALOG = 'groupRemoveDialog';
@@ -72,33 +66,6 @@ export const openRemoveGroupDialog = (uuid: string) =>
}));
};
-export interface RenameGroupFormData {
- [RENAME_GROUP_UUID_FIELD_NAME]: string;
- [RENAME_GROUP_NAME_FIELD_NAME]: string;
-}
-
-export const openRenameGroupDialog = (uuid: string) =>
- (dispatch: Dispatch, getState: () => RootState) => {
- const group = getResource<GroupResource>(uuid)(getState().resources);
-
- if (group) {
- const formData: RenameGroupFormData = {[RENAME_GROUP_UUID_FIELD_NAME]: group.uuid, [RENAME_GROUP_NAME_FIELD_NAME]: group.name};
- console.log("Initialize form: ", formData);
- dispatch(reset(RENAME_GROUP_FORM));
- dispatch<any>(initialize(RENAME_GROUP_FORM, formData));
- dispatch(dialogActions.OPEN_DIALOG({ id: RENAME_GROUP_DIALOG, data: group }));
- }
- };
-
-
-export const renameGroup = (data: RenameGroupFormData) =>
- async (dispatch: Dispatch, getState: () => RootState, { groupsService }: ServiceRepository) => {
- console.log("RenameGroupFormData", data);
- await groupsService.update(data[RENAME_GROUP_UUID_FIELD_NAME], { name: data[RENAME_GROUP_NAME_FIELD_NAME] });
- dispatch(dialogActions.CLOSE_DIALOG({ id: RENAME_GROUP_DIALOG }));
- dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Renamed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
- };
-
export interface CreateGroupFormData {
[CREATE_GROUP_NAME_FIELD_NAME]: string;
[CREATE_GROUP_USERS_FIELD_NAME]?: Participant[];
diff --git a/src/views-components/context-menu/action-sets/group-action-set.ts b/src/views-components/context-menu/action-sets/group-action-set.ts
index 3fbec27e..f2c9b92f 100644
--- a/src/views-components/context-menu/action-sets/group-action-set.ts
+++ b/src/views-components/context-menu/action-sets/group-action-set.ts
@@ -5,13 +5,14 @@
import { ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set";
import { RenameIcon, AdvancedIcon, RemoveIcon, AttributesIcon } from "components/icon/icon";
import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab";
-import { openRenameGroupDialog, openGroupAttributes, openRemoveGroupDialog } from "store/groups-panel/groups-panel-actions";
+import { openGroupAttributes, openRemoveGroupDialog } from "store/groups-panel/groups-panel-actions";
+import { openProjectUpdateDialog } from "store/projects/project-update-actions";
export const groupActionSet: ContextMenuActionSet = [[{
name: "Rename",
icon: RenameIcon,
- execute: (dispatch, { uuid }) => {
- dispatch<any>(openRenameGroupDialog(uuid));
+ execute: (dispatch, resource) => {
+ dispatch<any>(openProjectUpdateDialog(resource));
}
}, {
name: "Attributes",
diff --git a/src/views-components/dialog-forms/rename-group-dialog.tsx b/src/views-components/dialog-forms/rename-group-dialog.tsx
deleted file mode 100644
index 72f09d78..00000000
--- a/src/views-components/dialog-forms/rename-group-dialog.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import React from 'react';
-import { compose, Dispatch } from 'redux';
-import { reduxForm, InjectedFormProps, Field } from 'redux-form';
-import { withDialog, WithDialogProps } from 'store/dialog/with-dialog';
-import { FormDialog } from 'components/form-dialog/form-dialog';
-import { DialogContentText } from '@material-ui/core';
-import { TextField } from 'components/text-field/text-field';
-import { GroupResource } from 'models/group';
-import { RENAME_GROUP_DIALOG, RENAME_GROUP_NAME_FIELD_NAME, RenameGroupFormData, renameGroup } from 'store/groups-panel/groups-panel-actions';
-// import { WarningCollection } from 'components/warning-collection/warning-collection';
-import { RENAME_FILE_VALIDATION } from 'validators/validators';
-
-export const RenameGroupDialog = compose(
- withDialog(RENAME_GROUP_DIALOG),
- reduxForm<RenameGroupFormData>({
- form: RENAME_GROUP_DIALOG,
- // touchOnChange: true,
- onSubmit: (data: RenameGroupFormData, dispatch: Dispatch) => {
- console.log(data);
- // dispatch<any>(renameGroup(data));
- }
- })
-)((props: RenameGroupDialogProps) =>
- <FormDialog
- dialogTitle='Rename'
- formFields={RenameGroupFormFields}
- submitLabel='Ok'
- {...props}
- />);
-
-interface RenameGroupDataProps {
- data: GroupResource;
-}
-
-type RenameGroupDialogProps = RenameGroupDataProps & WithDialogProps<{}> & InjectedFormProps<RenameGroupFormData>;
-
-const RenameGroupFormFields = (props: RenameGroupDialogProps) => {
- // console.log(props);
- return <>
- <DialogContentText>
- {`Please enter a new name for ${props.data.name}`}
- </DialogContentText>
- <Field
- name={RENAME_GROUP_NAME_FIELD_NAME}
- component={TextField as any}
- autoFocus={true}
- validate={RENAME_FILE_VALIDATION}
- />
- {/* <WarningCollection text="Renaming a file will change the collection's content address." /> */}
- </>;
-}
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index d99b98ff..a9f59f68 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -21,7 +21,6 @@ import { RootState } from 'store/store';
import { openContextMenu } from 'store/context-menu/context-menu-actions';
import { ResourceKind } from 'models/resource';
import { LinkClass, LinkResource } from 'models/link';
-import { navigateToGroupDetails } from 'store/navigation/navigation-action';
export enum GroupsPanelColumnNames {
GROUP = "Name",
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 8b687632..50194f9e 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -90,7 +90,6 @@ import { RemoveGroupMemberDialog } from 'views-components/groups-dialog/member-r
import { GroupMemberAttributesDialog } from 'views-components/groups-dialog/member-attributes-dialog';
import { AddGroupMembersDialog } from 'views-components/dialog-forms/add-group-member-dialog';
import { EditPermissionLevelDialog } from 'views-components/dialog-forms/edit-permission-level-dialog';
-import { RenameGroupDialog } from 'views-components/dialog-forms/rename-group-dialog';
import { PartialCopyToCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-collection-dialog';
import { PublicFavoritePanel } from 'views/public-favorites-panel/public-favorites-panel';
import { LinkAccountPanel } from 'views/link-account-panel/link-account-panel';
@@ -215,7 +214,6 @@ export const WorkbenchPanel =
<DetailsPanel />
</Grid>
<AddGroupMembersDialog />
- <RenameGroupDialog />
<EditPermissionLevelDialog />
<AdvancedTabDialog />
<AttributesApiClientAuthorizationDialog />
commit de3aa7cd755ae48855c0a8031a67d66237755fe0
Author: Stephen Smith <stephen at curii.com>
Date: Tue Nov 9 15:40:06 2021 -0500
18123: Add group rename dialog
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/dialog-forms/rename-group-dialog.tsx b/src/views-components/dialog-forms/rename-group-dialog.tsx
new file mode 100644
index 00000000..72f09d78
--- /dev/null
+++ b/src/views-components/dialog-forms/rename-group-dialog.tsx
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import { compose, Dispatch } from 'redux';
+import { reduxForm, InjectedFormProps, Field } from 'redux-form';
+import { withDialog, WithDialogProps } from 'store/dialog/with-dialog';
+import { FormDialog } from 'components/form-dialog/form-dialog';
+import { DialogContentText } from '@material-ui/core';
+import { TextField } from 'components/text-field/text-field';
+import { GroupResource } from 'models/group';
+import { RENAME_GROUP_DIALOG, RENAME_GROUP_NAME_FIELD_NAME, RenameGroupFormData, renameGroup } from 'store/groups-panel/groups-panel-actions';
+// import { WarningCollection } from 'components/warning-collection/warning-collection';
+import { RENAME_FILE_VALIDATION } from 'validators/validators';
+
+export const RenameGroupDialog = compose(
+ withDialog(RENAME_GROUP_DIALOG),
+ reduxForm<RenameGroupFormData>({
+ form: RENAME_GROUP_DIALOG,
+ // touchOnChange: true,
+ onSubmit: (data: RenameGroupFormData, dispatch: Dispatch) => {
+ console.log(data);
+ // dispatch<any>(renameGroup(data));
+ }
+ })
+)((props: RenameGroupDialogProps) =>
+ <FormDialog
+ dialogTitle='Rename'
+ formFields={RenameGroupFormFields}
+ submitLabel='Ok'
+ {...props}
+ />);
+
+interface RenameGroupDataProps {
+ data: GroupResource;
+}
+
+type RenameGroupDialogProps = RenameGroupDataProps & WithDialogProps<{}> & InjectedFormProps<RenameGroupFormData>;
+
+const RenameGroupFormFields = (props: RenameGroupDialogProps) => {
+ // console.log(props);
+ return <>
+ <DialogContentText>
+ {`Please enter a new name for ${props.data.name}`}
+ </DialogContentText>
+ <Field
+ name={RENAME_GROUP_NAME_FIELD_NAME}
+ component={TextField as any}
+ autoFocus={true}
+ validate={RENAME_FILE_VALIDATION}
+ />
+ {/* <WarningCollection text="Renaming a file will change the collection's content address." /> */}
+ </>;
+}
commit 12d2c589092bc5cead8ded7ea2148949969bc477
Author: Stephen Smith <stephen at curii.com>
Date: Tue Nov 9 14:38:46 2021 -0500
18123: Partial group rename dialog
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index 099d046d..9c9f15cf 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from 'redux';
-import { reset, startSubmit, stopSubmit, FormErrors } from 'redux-form';
+import { reset, initialize, startSubmit, stopSubmit, FormErrors } from 'redux-form';
import { bindDataExplorerActions } from "store/data-explorer/data-explorer-action";
import { dialogActions } from 'store/dialog/dialog-actions';
import { Participant } from 'views-components/sharing-dialog/participant-select';
@@ -18,10 +18,19 @@ import { PermissionService } from 'services/permission-service/permission-servic
import { FilterBuilder } from 'services/api/filter-builder';
export const GROUPS_PANEL_ID = "groupsPanel";
+
+// Create group dialog
export const CREATE_GROUP_DIALOG = "createGroupDialog";
export const CREATE_GROUP_FORM = "createGroupForm";
export const CREATE_GROUP_NAME_FIELD_NAME = 'name';
export const CREATE_GROUP_USERS_FIELD_NAME = 'users';
+
+// Rename group dialog
+export const RENAME_GROUP_DIALOG = "renameGroupDialog";
+export const RENAME_GROUP_FORM = "renameGroupForm";
+export const RENAME_GROUP_UUID_FIELD_NAME = 'uuid';
+export const RENAME_GROUP_NAME_FIELD_NAME = 'name';
+
export const GROUP_ATTRIBUTES_DIALOG = 'groupAttributesDialog';
export const GROUP_REMOVE_DIALOG = 'groupRemoveDialog';
@@ -63,6 +72,33 @@ export const openRemoveGroupDialog = (uuid: string) =>
}));
};
+export interface RenameGroupFormData {
+ [RENAME_GROUP_UUID_FIELD_NAME]: string;
+ [RENAME_GROUP_NAME_FIELD_NAME]: string;
+}
+
+export const openRenameGroupDialog = (uuid: string) =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const group = getResource<GroupResource>(uuid)(getState().resources);
+
+ if (group) {
+ const formData: RenameGroupFormData = {[RENAME_GROUP_UUID_FIELD_NAME]: group.uuid, [RENAME_GROUP_NAME_FIELD_NAME]: group.name};
+ console.log("Initialize form: ", formData);
+ dispatch(reset(RENAME_GROUP_FORM));
+ dispatch<any>(initialize(RENAME_GROUP_FORM, formData));
+ dispatch(dialogActions.OPEN_DIALOG({ id: RENAME_GROUP_DIALOG, data: group }));
+ }
+ };
+
+
+export const renameGroup = (data: RenameGroupFormData) =>
+ async (dispatch: Dispatch, getState: () => RootState, { groupsService }: ServiceRepository) => {
+ console.log("RenameGroupFormData", data);
+ await groupsService.update(data[RENAME_GROUP_UUID_FIELD_NAME], { name: data[RENAME_GROUP_NAME_FIELD_NAME] });
+ dispatch(dialogActions.CLOSE_DIALOG({ id: RENAME_GROUP_DIALOG }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Renamed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
+ };
+
export interface CreateGroupFormData {
[CREATE_GROUP_NAME_FIELD_NAME]: string;
[CREATE_GROUP_USERS_FIELD_NAME]?: Participant[];
diff --git a/src/views-components/context-menu/action-sets/group-action-set.ts b/src/views-components/context-menu/action-sets/group-action-set.ts
index ad38cbeb..3fbec27e 100644
--- a/src/views-components/context-menu/action-sets/group-action-set.ts
+++ b/src/views-components/context-menu/action-sets/group-action-set.ts
@@ -3,11 +3,17 @@
// 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 { RenameIcon, AdvancedIcon, RemoveIcon, AttributesIcon } from "components/icon/icon";
import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab";
-import { openGroupAttributes, openRemoveGroupDialog } from "store/groups-panel/groups-panel-actions";
+import { openRenameGroupDialog, openGroupAttributes, openRemoveGroupDialog } from "store/groups-panel/groups-panel-actions";
export const groupActionSet: ContextMenuActionSet = [[{
+ name: "Rename",
+ icon: RenameIcon,
+ execute: (dispatch, { uuid }) => {
+ dispatch<any>(openRenameGroupDialog(uuid));
+ }
+}, {
name: "Attributes",
icon: AttributesIcon,
execute: (dispatch, { uuid }) => {
@@ -25,4 +31,4 @@ export const groupActionSet: ContextMenuActionSet = [[{
execute: (dispatch, { uuid }) => {
dispatch<any>(openRemoveGroupDialog(uuid));
}
-}]];
\ No newline at end of file
+}]];
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 50194f9e..8b687632 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -90,6 +90,7 @@ import { RemoveGroupMemberDialog } from 'views-components/groups-dialog/member-r
import { GroupMemberAttributesDialog } from 'views-components/groups-dialog/member-attributes-dialog';
import { AddGroupMembersDialog } from 'views-components/dialog-forms/add-group-member-dialog';
import { EditPermissionLevelDialog } from 'views-components/dialog-forms/edit-permission-level-dialog';
+import { RenameGroupDialog } from 'views-components/dialog-forms/rename-group-dialog';
import { PartialCopyToCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-collection-dialog';
import { PublicFavoritePanel } from 'views/public-favorites-panel/public-favorites-panel';
import { LinkAccountPanel } from 'views/link-account-panel/link-account-panel';
@@ -214,6 +215,7 @@ export const WorkbenchPanel =
<DetailsPanel />
</Grid>
<AddGroupMembersDialog />
+ <RenameGroupDialog />
<EditPermissionLevelDialog />
<AdvancedTabDialog />
<AttributesApiClientAuthorizationDialog />
commit 5d0a0226a4ea4bb98f35d4ce76698f7a6606bfb4
Author: Stephen Smith <stephen at curii.com>
Date: Tue Nov 9 14:37:51 2021 -0500
18123: Fix extraneous props.dispatch being passed to Typography warning
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index 98dc78ee..d99b98ff 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -127,4 +127,4 @@ const GroupMembersCount = connect(
};
}
-)(Typography);
+)((props: {children: number}) => (<Typography children={props.children} />));
commit 1dcbe2d0ec3fdc613edca3a490d0adfce023a803
Author: Stephen Smith <stephen at curii.com>
Date: Tue Nov 9 14:29:50 2021 -0500
18123: Remove unnecessary cick handlers
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index 50838f7d..b4a8c6d3 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -153,8 +153,8 @@ export const GroupDetailsPanel = connect(
id={GROUP_DETAILS_MEMBERS_PANEL_ID}
onRowClick={noop}
onRowDoubleClick={noop}
- onContextMenu={this.handleContextMenu}
- contextMenuColumn={true}
+ onContextMenu={noop}
+ contextMenuColumn={false}
hideColumnSelector
hideSearchInput
actions={
@@ -176,8 +176,8 @@ export const GroupDetailsPanel = connect(
id={GROUP_DETAILS_PERMISSIONS_PANEL_ID}
onRowClick={noop}
onRowDoubleClick={noop}
- onContextMenu={this.handleContextMenu}
- contextMenuColumn={true}
+ onContextMenu={noop}
+ contextMenuColumn={false}
hideColumnSelector
hideSearchInput
paperProps={{
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index 9bfad524..98dc78ee 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -62,15 +62,12 @@ const mapStateToProps = (state: RootState) => {
const mapDispatchToProps = {
onContextMenu: openContextMenu,
- onRowDoubleClick: (uuid: string) =>
- navigateToGroupDetails(uuid),
onNewGroup: openCreateGroupDialog,
};
export interface GroupsPanelProps {
onNewGroup: () => void;
onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
- onRowDoubleClick: (item: string) => void;
resources: ResourcesState;
}
@@ -84,7 +81,7 @@ export const GroupsPanel = connect(
<DataExplorer
id={GROUPS_PANEL_ID}
onRowClick={noop}
- onRowDoubleClick={this.props.onRowDoubleClick}
+ onRowDoubleClick={noop}
onContextMenu={this.handleContextMenu}
contextMenuColumn={true}
hideColumnSelector
commit 1277b2a092fbd057220ee43d6fc47bffff5933d1
Author: Stephen Smith <stephen at curii.com>
Date: Mon Nov 8 10:09:33 2021 -0500
18123: Add edit permission level dialog for group members and outgoing permissions.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts
index 22247a8f..26ba537d 100644
--- a/src/store/group-details-panel/group-details-panel-actions.ts
+++ b/src/store/group-details-panel/group-details-panel-actions.ts
@@ -8,14 +8,16 @@ import { propertiesActions } from 'store/properties/properties-actions';
import { getProperty } from 'store/properties/properties';
import { Participant } from 'views-components/sharing-dialog/participant-select';
import { dialogActions } from 'store/dialog/dialog-actions';
-import { reset, startSubmit } from 'redux-form';
+import { initialize, reset, startSubmit } from 'redux-form';
import { addGroupMember, deleteGroupMember } from 'store/groups-panel/groups-panel-actions';
import { getResource } from 'store/resources/resources';
import { GroupResource } from 'models/group';
+import { Resource } from 'models/resource';
import { RootState } from 'store/store';
import { ServiceRepository } from 'services/services';
-import { PermissionResource } from 'models/permission';
+import { PermissionResource, PermissionLevel } from 'models/permission';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
+import { PermissionSelectValue, parsePermissionLevel, formatPermissionLevel } from 'views-components/sharing-dialog/permission-select';
export const GROUP_DETAILS_MEMBERS_PANEL_ID = 'groupDetailsMembersPanel';
export const GROUP_DETAILS_PERMISSIONS_PANEL_ID = 'groupDetailsPermissionsPanel';
@@ -24,6 +26,10 @@ export const ADD_GROUP_MEMBERS_FORM = 'addGroupMembers';
export const ADD_GROUP_MEMBERS_USERS_FIELD_NAME = 'users';
export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog';
export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog';
+export const EDIT_PERMISSION_LEVEL_DIALOG = 'editPermissionLevel';
+export const EDIT_PERMISSION_LEVEL_FORM = 'editPermissionLevel';
+export const EDIT_PERMISSION_LEVEL_FIELD_NAME = 'name';
+export const EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME = 'uuid';
export const GroupMembersPanelActions = bindDataExplorerActions(GROUP_DETAILS_MEMBERS_PANEL_ID);
export const GroupPermissionsPanelActions = bindDataExplorerActions(GROUP_DETAILS_PERMISSIONS_PANEL_ID);
@@ -42,6 +48,11 @@ export interface AddGroupMembersFormData {
[ADD_GROUP_MEMBERS_USERS_FIELD_NAME]: Participant[];
}
+export interface EditPermissionLevelFormData {
+ [EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME]: string;
+ [EDIT_PERMISSION_LEVEL_FIELD_NAME]: PermissionSelectValue;
+}
+
export const openAddGroupMembersDialog = () =>
(dispatch: Dispatch) => {
dispatch(dialogActions.OPEN_DIALOG({ id: ADD_GROUP_MEMBERS_DIALOG, data: {} }));
@@ -80,6 +91,32 @@ export const addGroupMembers = ({ users }: AddGroupMembersFormData) =>
}
};
+export const openEditPermissionLevelDialog = (linkUuid: string, resourceUuid: string) =>
+ async (dispatch: Dispatch, getState: () => RootState) => {
+ const link = getResource<PermissionResource>(linkUuid)(getState().resources);
+ const resource = getResource<Resource>(resourceUuid)(getState().resources);
+
+ if (link) {
+ dispatch(reset(EDIT_PERMISSION_LEVEL_FORM));
+ dispatch<any>(initialize(EDIT_PERMISSION_LEVEL_FORM, {[EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME]: link.uuid, [EDIT_PERMISSION_LEVEL_FIELD_NAME]: formatPermissionLevel(link.name as PermissionLevel)}));
+ dispatch(dialogActions.OPEN_DIALOG({ id: EDIT_PERMISSION_LEVEL_DIALOG, data: resource }));
+ }
+ };
+
+export const editPermissionLevel = (data: EditPermissionLevelFormData) =>
+ async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => {
+ try {
+ await permissionService.update(data[EDIT_PERMISSION_LEVEL_UUID_FIELD_NAME], {name: parsePermissionLevel(data[EDIT_PERMISSION_LEVEL_FIELD_NAME])});
+ dispatch(dialogActions.CLOSE_DIALOG({ id: EDIT_PERMISSION_LEVEL_DIALOG }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Permission level changed.', hideDuration: 2000 }));
+ } catch (e) {
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: 'Failed to update permission',
+ kind: SnackbarKind.ERROR,
+ }));
+ }
+ };
+
export const openGroupMemberAttributes = (uuid: string) =>
(dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const { resources } = getState();
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 71b82b6f..73ef32b0 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -6,7 +6,7 @@ import React from 'react';
import { Grid, Typography, withStyles, Tooltip, IconButton, Checkbox } from '@material-ui/core';
import { FavoriteStar, PublicFavoriteStar } from '../favorite-star/favorite-star';
import { Resource, ResourceKind, TrashableResource } from 'models/resource';
-import { ProjectIcon, FilterGroupIcon, CollectionIcon, ProcessIcon, DefaultIcon, ShareIcon, CollectionOldVersionIcon, WorkflowIcon, RemoveIcon } from 'components/icon/icon';
+import { ProjectIcon, FilterGroupIcon, CollectionIcon, ProcessIcon, DefaultIcon, ShareIcon, CollectionOldVersionIcon, WorkflowIcon, RemoveIcon, RenameIcon } from 'components/icon/icon';
import { formatDate, formatFileSize, formatTime } from 'common/formatters';
import { resourceLabel } from 'common/labels';
import { connect, DispatchProp } from 'react-redux';
@@ -23,21 +23,25 @@ import { openSharingDialog } from 'store/sharing-dialog/sharing-dialog-actions';
import { getUserFullname, getUserDisplayName, User, UserResource } from 'models/user';
import { toggleIsActive, toggleIsAdmin } from 'store/users/users-actions';
import { LinkResource } from 'models/link';
-import { navigateTo } from 'store/navigation/navigation-action';
+import { navigateTo, navigateToGroupDetails } from 'store/navigation/navigation-action';
import { withResourceData } from 'views-components/data-explorer/with-resources';
import { CollectionResource } from 'models/collection';
import { IllegalNamingWarning } from 'components/warning/warning';
import { loadResource } from 'store/resources/resources-actions';
import { GroupClass } from 'models/group';
-import { openRemoveGroupMemberDialog } from 'store/group-details-panel/group-details-panel-actions';
+import { openRemoveGroupMemberDialog, openEditPermissionLevelDialog } from 'store/group-details-panel/group-details-panel-actions';
+import { formatPermissionLevel } from 'views-components/sharing-dialog/permission-select';
+import { PermissionLevel } from 'models/permission';
-const renderName = (dispatch: Dispatch, item: GroupContentsResource) =>
- <Grid container alignItems="center" wrap="nowrap" spacing={16}>
+const renderName = (dispatch: Dispatch, item: GroupContentsResource) => {
+
+ const navFunc = ("groupClass" in item && item.groupClass === GroupClass.ROLE ? navigateToGroupDetails : navigateTo);
+ return <Grid container alignItems="center" wrap="nowrap" spacing={16}>
<Grid item>
{renderIcon(item)}
</Grid>
<Grid item>
- <Typography color="primary" style={{ width: 'auto', cursor: 'pointer' }} onClick={() => dispatch<any>(navigateTo(item.uuid))}>
+ <Typography color="primary" style={{ width: 'auto', cursor: 'pointer' }} onClick={() => dispatch<any>(navFunc(item.uuid))}>
{item.kind === ResourceKind.PROJECT || item.kind === ResourceKind.COLLECTION
? <IllegalNamingWarning name={item.name} />
: null}
@@ -51,6 +55,7 @@ const renderName = (dispatch: Dispatch, item: GroupContentsResource) =>
</Typography>
</Grid>
</Grid>;
+};
export const ResourceName = connect(
(state: RootState, props: { uuid: string }) => {
@@ -287,21 +292,31 @@ export const ResourceLinkClass = connect(
return resource || { linkClass: '' };
})(renderLinkClass);
-const renderLink = (dispatch: Dispatch, item: Resource) => {
- var displayName = '';
-
- if ((item as UserResource).kind === ResourceKind.USER
- && typeof (item as UserResource).firstName !== 'undefined') {
+const getResourceDisplayName = (resource: Resource): string => {
+ if ((resource as UserResource).kind === ResourceKind.USER
+ && typeof (resource as UserResource).firstName !== 'undefined') {
// We can be sure the resource is UserResource
- displayName = getUserDisplayName(item as UserResource);
+ return getUserDisplayName(resource as UserResource);
} else {
- displayName = (item as GroupContentsResource).name;
+ return (resource as GroupContentsResource).name;
}
+}
+
+const renderResourceLink = (dispatch: Dispatch, item: Resource) => {
+ var displayName = getResourceDisplayName(item);
return <Typography noWrap color="primary" style={{ 'cursor': 'pointer' }} onClick={() => dispatch<any>(navigateTo(item.uuid))}>
{resourceLabel(item.kind)}: {displayName || item.uuid}
</Typography>;
-}
+};
+
+const renderResource = (dispatch: Dispatch, item: Resource) => {
+ var displayName = getResourceDisplayName(item);
+
+ return <Typography variant='body2'>
+ {resourceLabel(item.kind)}: {displayName || item.uuid}
+ </Typography>;
+};
export const ResourceLinkTail = connect(
(state: RootState, props: { uuid: string }) => {
@@ -312,7 +327,7 @@ export const ResourceLinkTail = connect(
item: tailResource || { uuid: resource?.tailUuid || '', kind: resource?.tailKind || ResourceKind.NONE }
};
})((props: { item: Resource } & DispatchProp<any>) =>
- renderLink(props.dispatch, props.item));
+ renderResourceLink(props.dispatch, props.item));
export const ResourceLinkHead = connect(
(state: RootState, props: { uuid: string }) => {
@@ -323,7 +338,7 @@ export const ResourceLinkHead = connect(
item: headResource || { uuid: resource?.headUuid || '', kind: resource?.headKind || ResourceKind.NONE }
};
})((props: { item: Resource } & DispatchProp<any>) =>
- renderLink(props.dispatch, props.item));
+ renderResourceLink(props.dispatch, props.item));
export const ResourceLinkUuid = connect(
(state: RootState, props: { uuid: string }) => {
@@ -384,6 +399,49 @@ export const ResourceLinkTailUsername = connect(
return resource || { username: '' };
})(renderUsername);
+const renderPermissionLevel = (dispatch: Dispatch, link: LinkResource, resource: Resource) => {
+ return <Typography noWrap>
+ {formatPermissionLevel(link.name as PermissionLevel)}
+ <IconButton onClick={() => dispatch<any>(openEditPermissionLevelDialog(link.uuid, resource.uuid))}>
+ <RenameIcon />
+ </IconButton>
+ </Typography>;
+}
+
+export const ResourceLinkHeadPermissionLevel = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<Resource>(link?.headUuid || '')(state.resources);
+
+ return {
+ link: link || { uuid: '', name: '', kind: ResourceKind.NONE },
+ resource: resource || { uuid: '', kind: ResourceKind.NONE }
+ };
+ })((props: { link: LinkResource, resource: Resource } & DispatchProp<any>) =>
+ renderPermissionLevel(props.dispatch, props.link, props.resource));
+
+export const ResourceLinkTailPermissionLevel = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<Resource>(link?.tailUuid || '')(state.resources);
+
+ return {
+ link: link || { uuid: '', name: '', kind: ResourceKind.NONE },
+ resource: resource || { uuid: '', kind: ResourceKind.NONE }
+ };
+ })((props: { link: LinkResource, resource: Resource } & DispatchProp<any>) =>
+ renderPermissionLevel(props.dispatch, props.link, props.resource));
+
+// Displays resource type and display name without link
+export const ResourceLabel = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<Resource>(props.uuid)(state.resources);
+ return {
+ item: resource || { uuid: '', kind: ResourceKind.NONE }
+ };
+ })((props: { item: Resource } & DispatchProp<any>) =>
+ renderResource(props.dispatch, props.item));
+
// Process Resources
const resourceRunProcess = (dispatch: Dispatch, uuid: string) => {
return (
diff --git a/src/views-components/dialog-forms/edit-permission-level-dialog.tsx b/src/views-components/dialog-forms/edit-permission-level-dialog.tsx
new file mode 100644
index 00000000..5479a0c6
--- /dev/null
+++ b/src/views-components/dialog-forms/edit-permission-level-dialog.tsx
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import { compose } from "redux";
+import { reduxForm, InjectedFormProps, WrappedFieldProps, Field } from 'redux-form';
+import { withDialog, WithDialogProps } from "store/dialog/with-dialog";
+import { FormDialog } from 'components/form-dialog/form-dialog';
+import { EDIT_PERMISSION_LEVEL_DIALOG, EDIT_PERMISSION_LEVEL_FORM, EditPermissionLevelFormData, EDIT_PERMISSION_LEVEL_FIELD_NAME, editPermissionLevel } from 'store/group-details-panel/group-details-panel-actions';
+import { require } from 'validators/require';
+import { PermissionSelect } from 'views-components/sharing-dialog/permission-select';
+import { Grid } from '@material-ui/core';
+import { Resource } from 'models/resource';
+import { ResourceLabel } from 'views-components/data-explorer/renderers';
+
+export const EditPermissionLevelDialog = compose(
+ withDialog(EDIT_PERMISSION_LEVEL_DIALOG),
+ reduxForm<EditPermissionLevelFormData>({
+ form: EDIT_PERMISSION_LEVEL_FORM,
+ onSubmit: (data, dispatch) => {
+ dispatch(editPermissionLevel(data));
+ },
+ })
+)(
+ (props: EditPermissionLevelDialogProps) =>
+ <FormDialog
+ dialogTitle='Edit permission'
+ formFields={PermissionField}
+ submitLabel='Update'
+ {...props}
+ />
+);
+
+interface EditPermissionLevelDataProps {
+ data: Resource;
+}
+
+type EditPermissionLevelDialogProps = EditPermissionLevelDataProps & WithDialogProps<{}> & InjectedFormProps<EditPermissionLevelFormData>;
+
+const PermissionField = (props: EditPermissionLevelDialogProps) =>
+ <Grid container spacing={8}>
+ <Grid item xs={8}>
+ <ResourceLabel uuid={props.data.uuid} />
+ </Grid>
+ <Grid item xs={4} container wrap='nowrap'>
+ <Field
+ name={EDIT_PERMISSION_LEVEL_FIELD_NAME}
+ component={PermissionSelectComponent as any}
+ validate={require} />
+ </Grid>
+ </Grid>;
+
+const PermissionSelectComponent = ({ input }: WrappedFieldProps) =>
+ <PermissionSelect fullWidth disableUnderline {...input} />;
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index c402ebb6..50838f7d 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, ResourceLinkTailUsername, ResourceLinkName, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete } from 'views-components/data-explorer/renderers';
+import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, ResourceLinkTailUsername, ResourceLinkHeadPermissionLevel, ResourceLinkTailPermissionLevel, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
@@ -62,7 +62,7 @@ export const groupDetailsMembersPanelColumns: DataColumns<string> = [
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceLinkName uuid={uuid} />
+ render: uuid => <ResourceLinkTailPermissionLevel uuid={uuid} />
},
{
name: GroupDetailsPanelMembersColumnNames.UUID,
@@ -93,7 +93,7 @@ export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceLinkName uuid={uuid} />
+ render: uuid => <ResourceLinkHeadPermissionLevel uuid={uuid} />
},
{
name: GroupDetailsPanelPermissionsColumnNames.UUID,
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index 04f2a273..9bfad524 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -8,7 +8,7 @@ import { Grid, Button, Typography } from "@material-ui/core";
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
import { SortDirection } from 'components/data-table/data-column';
-import { ResourceOwner } from 'views-components/data-explorer/renderers';
+import { ResourceUuid } from 'views-components/data-explorer/renderers';
import { AddIcon } from 'components/icon/icon';
import { ResourceName } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
@@ -25,7 +25,7 @@ import { navigateToGroupDetails } from 'store/navigation/navigation-action';
export enum GroupsPanelColumnNames {
GROUP = "Name",
- OWNER = "Owner",
+ UUID = "UUID",
MEMBERS = "Members",
}
@@ -39,11 +39,11 @@ export const groupsPanelColumns: DataColumns<string> = [
render: uuid => <ResourceName uuid={uuid} />
},
{
- name: GroupsPanelColumnNames.OWNER,
+ name: GroupsPanelColumnNames.UUID,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceOwner uuid={uuid} />,
+ render: uuid => <ResourceUuid uuid={uuid} />,
},
{
name: GroupsPanelColumnNames.MEMBERS,
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 9ce93bf2..50194f9e 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -89,6 +89,7 @@ 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';
import { AddGroupMembersDialog } from 'views-components/dialog-forms/add-group-member-dialog';
+import { EditPermissionLevelDialog } from 'views-components/dialog-forms/edit-permission-level-dialog';
import { PartialCopyToCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-collection-dialog';
import { PublicFavoritePanel } from 'views/public-favorites-panel/public-favorites-panel';
import { LinkAccountPanel } from 'views/link-account-panel/link-account-panel';
@@ -213,6 +214,7 @@ export const WorkbenchPanel =
<DetailsPanel />
</Grid>
<AddGroupMembersDialog />
+ <EditPermissionLevelDialog />
<AdvancedTabDialog />
<AttributesApiClientAuthorizationDialog />
<AttributesKeepServiceDialog />
commit b565525602ad5203d313abe6b898f1885e344abc
Author: Stephen Smith <stephen at curii.com>
Date: Thu Nov 4 10:17:05 2021 -0400
18123: Filter groups list to type role instead of excluding all other types.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-middleware-service.ts b/src/store/groups-panel/groups-panel-middleware-service.ts
index c157e9ab..28415506 100644
--- a/src/store/groups-panel/groups-panel-middleware-service.ts
+++ b/src/store/groups-panel/groups-panel-middleware-service.ts
@@ -36,7 +36,7 @@ export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService
order.addOrder(direction, 'name');
}
const filters = new FilterBuilder()
- .addNotIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER])
+ .addEqual('group_class', GroupClass.ROLE)
.addILike('name', dataExplorer.searchValue)
.getFilters();
const response = await this.services.groupsService
commit d6c180028671059f19912a11887b804e9d63d608
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 3 20:57:53 2021 -0400
18123: Allow hiding search box while actions are present for group details.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx
index d272e870..f3cccfce 100644
--- a/src/components/data-explorer/data-explorer.tsx
+++ b/src/components/data-explorer/data-explorer.tsx
@@ -98,14 +98,14 @@ export const DataExplorer = withStyles(styles)(
} = this.props;
return <Paper className={classes.root} {...paperProps} key={paperKey}>
{title && <div className={classes.title}>{title}</div>}
- {(!hideColumnSelector || !hideSearchInput) && <Toolbar className={title ? classes.toolbarUnderTitle : classes.toolbar}>
+ {(!hideColumnSelector || !hideSearchInput || !!actions) && <Toolbar className={title ? classes.toolbarUnderTitle : classes.toolbar}>
<Grid container justify="space-between" wrap="nowrap" alignItems="center">
- <div className={classes.searchBox}>
+ {!hideSearchInput && <div className={classes.searchBox}>
{!hideSearchInput && <SearchInput
label={searchLabel}
value={searchValue}
onSearch={onSearch} />}
- </div>
+ </div>}
{actions}
{!hideColumnSelector && <ColumnSelector
columns={columns}
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index f292b97b..c402ebb6 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -180,16 +180,6 @@ export const GroupDetailsPanel = connect(
contextMenuColumn={true}
hideColumnSelector
hideSearchInput
- actions={
- <Grid container justify='flex-end'>
- <Button
- variant="contained"
- color="primary"
- onClick={this.props.onAddUser}>
- <AddIcon /> Add user
- </Button>
- </Grid>
- }
paperProps={{
elevation: 0,
}} />
commit 703a2e1813ed1ff80d2ccd3214233240802b4754
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 3 20:39:23 2021 -0400
18123: Fix directionality of link tail renderer.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 77b5b694..71b82b6f 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -309,7 +309,7 @@ export const ResourceLinkTail = connect(
const tailResource = getResource<Resource>(resource?.tailUuid || '')(state.resources);
return {
- item: tailResource || { uuid: resource?.tailUuid || '', kind: resource?.headKind || ResourceKind.NONE }
+ item: tailResource || { uuid: resource?.tailUuid || '', kind: resource?.tailKind || ResourceKind.NONE }
};
})((props: { item: Resource } & DispatchProp<any>) =>
renderLink(props.dispatch, props.item));
commit 8d40870a23aa60855ed4b1a43a0186b7d50c0d7d
Author: Stephen Smith <stephen at curii.com>
Date: Wed Nov 3 20:35:01 2021 -0400
18123: Add role group class to model to fix creating groups.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/models/group.ts b/src/models/group.ts
index 365e9cce..7d144a58 100644
--- a/src/models/group.ts
+++ b/src/models/group.ts
@@ -17,4 +17,5 @@ export interface GroupResource extends TrashableResource {
export enum GroupClass {
PROJECT = 'project',
FILTER = 'filter',
+ ROLE = 'role',
}
diff --git a/src/models/project.ts b/src/models/project.ts
index 86ac04f6..b47b426f 100644
--- a/src/models/project.ts
+++ b/src/models/project.ts
@@ -5,7 +5,7 @@
import { GroupClass, GroupResource } from "./group";
export interface ProjectResource extends GroupResource {
- groupClass: GroupClass.PROJECT | GroupClass.FILTER;
+ groupClass: GroupClass.PROJECT | GroupClass.FILTER | GroupClass.ROLE;
}
export const getProjectUrl = (uuid: string) => {
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index dcf81f2d..099d046d 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -10,7 +10,7 @@ import { Participant } from 'views-components/sharing-dialog/participant-select'
import { RootState } from 'store/store';
import { ServiceRepository } from 'services/services';
import { getResource } from 'store/resources/resources';
-import { GroupResource } from 'models/group';
+import { GroupResource, GroupClass } 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';
@@ -72,7 +72,7 @@ export const createGroup = ({ name, users = [] }: CreateGroupFormData) =>
async (dispatch: Dispatch, _: {}, { groupsService, permissionService }: ServiceRepository) => {
dispatch(startSubmit(CREATE_GROUP_FORM));
try {
- const newGroup = await groupsService.create({ name });
+ const newGroup = await groupsService.create({ name, groupClass: GroupClass.ROLE });
for (const user of users) {
await addGroupMember({
user,
commit 798064c6616d98f8b6b3dfe562458bffe662f4ad
Author: Stephen Smith <stephen at curii.com>
Date: Mon Oct 25 10:26:50 2021 -0400
18123: Add group data table renderers
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 05ae4ce2..77b5b694 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -6,7 +6,7 @@ import React from 'react';
import { Grid, Typography, withStyles, Tooltip, IconButton, Checkbox } from '@material-ui/core';
import { FavoriteStar, PublicFavoriteStar } from '../favorite-star/favorite-star';
import { Resource, ResourceKind, TrashableResource } from 'models/resource';
-import { ProjectIcon, FilterGroupIcon, CollectionIcon, ProcessIcon, DefaultIcon, ShareIcon, CollectionOldVersionIcon, WorkflowIcon } from 'components/icon/icon';
+import { ProjectIcon, FilterGroupIcon, CollectionIcon, ProcessIcon, DefaultIcon, ShareIcon, CollectionOldVersionIcon, WorkflowIcon, RemoveIcon } from 'components/icon/icon';
import { formatDate, formatFileSize, formatTime } from 'common/formatters';
import { resourceLabel } from 'common/labels';
import { connect, DispatchProp } from 'react-redux';
@@ -29,6 +29,7 @@ import { CollectionResource } from 'models/collection';
import { IllegalNamingWarning } from 'components/warning/warning';
import { loadResource } from 'store/resources/resources-actions';
import { GroupClass } from 'models/group';
+import { openRemoveGroupMemberDialog } from 'store/group-details-panel/group-details-panel-actions';
const renderName = (dispatch: Dispatch, item: GroupContentsResource) =>
<Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -286,20 +287,6 @@ export const ResourceLinkClass = connect(
return resource || { linkClass: '' };
})(renderLinkClass);
-// const renderLinkTail = (dispatch: Dispatch, item: { uuid: string, tailUuid: string, tailKind: string }) => {
-// const currentLabel = resourceLabel(item.tailKind);
-// const isUnknow = currentLabel === "Unknown";
-// return (<div>
-// {!isUnknow ? (
-// renderLink(dispatch, item.tailUuid, "name", currentLabel)
-// ) : (
-// <Typography noWrap color="default">
-// {item.tailUuid}
-// </Typography>
-// )}
-// </div>);
-// };
-
const renderLink = (dispatch: Dispatch, item: Resource) => {
var displayName = '';
@@ -344,6 +331,59 @@ export const ResourceLinkUuid = connect(
return resource || { uuid: '' };
})(renderUuid);
+export const ResourceLinkHeadUuid = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const headResource = getResource<Resource>(link?.headUuid || '')(state.resources);
+
+ return headResource || { uuid: '' };
+ })(renderUuid);
+
+export const ResourceLinkTailUuid = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const tailResource = getResource<Resource>(link?.tailUuid || '')(state.resources);
+
+ return tailResource || { uuid: '' };
+ })(renderUuid);
+
+const renderLinkDelete = (dispatch: Dispatch, item: LinkResource) => {
+ if (item.uuid) {
+ return <Typography noWrap>
+ <IconButton onClick={() => dispatch<any>(openRemoveGroupMemberDialog(item.uuid))}>
+ <RemoveIcon />
+ </IconButton>
+ </Typography>;
+ } else {
+ return <Typography noWrap></Typography>;
+ }
+}
+
+export const ResourceLinkDelete = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ return {
+ item: link || { uuid: '', kind: ResourceKind.NONE }
+ };
+ })((props: { item: LinkResource } & DispatchProp<any>) =>
+ renderLinkDelete(props.dispatch, props.item));
+
+export const ResourceLinkTailEmail = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<UserResource>(link?.tailUuid || '')(state.resources);
+
+ return resource || { email: '' };
+ })(renderEmail);
+
+export const ResourceLinkTailUsername = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const link = getResource<LinkResource>(props.uuid)(state.resources);
+ const resource = getResource<UserResource>(link?.tailUuid || '')(state.resources);
+
+ return resource || { username: '' };
+ })(renderUsername);
+
// Process Resources
const resourceRunProcess = (dispatch: Dispatch, uuid: string) => {
return (
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index 5b1a884e..f292b97b 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceUuid, ResourceEmail, ResourceUsername, ResourceLinkName, ResourceLinkHead, ResourceLinkTail } from 'views-components/data-explorer/renderers';
+import { ResourceLinkHeadUuid, ResourceLinkTailUuid, ResourceLinkTailEmail, ResourceLinkTailUsername, ResourceLinkName, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
@@ -24,12 +24,15 @@ export enum GroupDetailsPanelMembersColumnNames {
UUID = "UUID",
EMAIL = "Email",
USERNAME = "Username",
+ PERMISSION = "Permission",
+ REMOVE = "Remove",
}
export enum GroupDetailsPanelPermissionsColumnNames {
- HEAD = "Head",
NAME = "Name",
+ PERMISSION = "Permission",
UUID = "UUID",
+ REMOVE = "Remove",
}
export const groupDetailsMembersPanelColumns: DataColumns<string> = [
@@ -45,34 +48,48 @@ export const groupDetailsMembersPanelColumns: DataColumns<string> = [
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceUsername uuid={uuid} />
+ render: uuid => <ResourceLinkTailUsername uuid={uuid} />
+ },
+ {
+ name: GroupDetailsPanelMembersColumnNames.EMAIL,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkTailEmail uuid={uuid} />
+ },
+ {
+ name: GroupDetailsPanelMembersColumnNames.PERMISSION,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkName uuid={uuid} />
},
{
name: GroupDetailsPanelMembersColumnNames.UUID,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceUuid uuid={uuid} />
+ render: uuid => <ResourceLinkTailUuid uuid={uuid} />
},
{
- name: GroupDetailsPanelMembersColumnNames.EMAIL,
+ name: GroupDetailsPanelMembersColumnNames.REMOVE,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceEmail uuid={uuid} />
+ render: uuid => <ResourceLinkDelete uuid={uuid} />
},
];
export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
{
- name: GroupDetailsPanelPermissionsColumnNames.HEAD,
+ name: GroupDetailsPanelPermissionsColumnNames.NAME,
selected: true,
configurable: true,
filters: createTree(),
render: uuid => <ResourceLinkHead uuid={uuid} />
},
{
- name: GroupDetailsPanelPermissionsColumnNames.NAME,
+ name: GroupDetailsPanelPermissionsColumnNames.PERMISSION,
selected: true,
configurable: true,
filters: createTree(),
@@ -83,7 +100,14 @@ export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceUuid uuid={uuid} />
+ render: uuid => <ResourceLinkHeadUuid uuid={uuid} />
+ },
+ {
+ name: GroupDetailsPanelPermissionsColumnNames.REMOVE,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkDelete uuid={uuid} />
},
];
commit 0399c993a117c3489f1fe0160d6a554a56b8bbab
Author: Stephen Smith <stephen at curii.com>
Date: Mon Oct 25 10:24:43 2021 -0400
18123: Fix directionality of queried links for group member count
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/groups-panel/groups-panel-middleware-service.ts b/src/store/groups-panel/groups-panel-middleware-service.ts
index 5fb4718d..c157e9ab 100644
--- a/src/store/groups-panel/groups-panel-middleware-service.ts
+++ b/src/store/groups-panel/groups-panel-middleware-service.ts
@@ -52,7 +52,7 @@ export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService
}));
const permissions = await this.services.permissionService.list({
filters: new FilterBuilder()
- .addIn('tail_uuid', response.items.map(item => item.uuid))
+ .addIn('head_uuid', response.items.map(item => item.uuid))
.getFilters()
});
api.dispatch(updateResources(permissions.items));
@@ -74,4 +74,3 @@ const couldNotFetchFavoritesContents = () =>
message: 'Could not fetch groups.',
kind: SnackbarKind.ERROR
});
-
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index 4d15118c..04f2a273 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -122,7 +122,7 @@ const GroupMembersCount = connect(
const permissions = filterResources((resource: LinkResource) =>
resource.kind === ResourceKind.LINK &&
resource.linkClass === LinkClass.PERMISSION &&
- resource.tailUuid === props.uuid
+ resource.headUuid === props.uuid
)(state.resources);
return {
commit acceec90afa2a2b5007ab75795c911ac75446bc4
Author: Stephen Smith <stephen at curii.com>
Date: Mon Oct 25 10:07:37 2021 -0400
18123: Filter uuids by kind before querying API
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts
index b9ac0d79..22247a8f 100644
--- a/src/store/group-details-panel/group-details-panel-actions.ts
+++ b/src/store/group-details-panel/group-details-panel-actions.ts
@@ -19,8 +19,8 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
export const GROUP_DETAILS_MEMBERS_PANEL_ID = 'groupDetailsMembersPanel';
export const GROUP_DETAILS_PERMISSIONS_PANEL_ID = 'groupDetailsPermissionsPanel';
-export const ADD_GROUP_MEMBERS_DIALOG = 'addGrupMembers';
-export const ADD_GROUP_MEMBERS_FORM = 'addGrupMembers';
+export const ADD_GROUP_MEMBERS_DIALOG = 'addGroupMembers';
+export const ADD_GROUP_MEMBERS_FORM = 'addGroupMembers';
export const ADD_GROUP_MEMBERS_USERS_FIELD_NAME = 'users';
export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog';
export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog';
diff --git a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
index 05a12d22..36e3c735 100644
--- a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
@@ -12,6 +12,7 @@ import { FilterBuilder } from 'services/api/filter-builder';
import { updateResources } from 'store/resources/resources-actions';
import { getCurrentGroupDetailsPanelUuid, GroupMembersPanelActions } from 'store/group-details-panel/group-details-panel-actions';
import { LinkClass } from 'models/link';
+import { ResourceKind } from 'models/resource';
export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddlewareService {
@@ -41,7 +42,9 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl
const usersIn = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
+ .addIn('uuid', permissionsIn.items
+ .filter((item) => item.tailKind === ResourceKind.USER)
+ .map(item => item.tailUuid))
.getFilters(),
count: "none"
});
@@ -49,7 +52,9 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl
const projectsIn = await this.services.projectService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
+ .addIn('uuid', permissionsIn.items
+ .filter((item) => item.tailKind === ResourceKind.PROJECT)
+ .map(item => item.tailUuid))
.getFilters(),
count: "none"
});
diff --git a/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
index 7ce2d380..9e41409d 100644
--- a/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
@@ -12,6 +12,7 @@ import { FilterBuilder } from 'services/api/filter-builder';
import { updateResources } from 'store/resources/resources-actions';
import { getCurrentGroupDetailsPanelUuid, GroupPermissionsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
import { LinkClass } from 'models/link';
+import { ResourceKind } from 'models/resource';
export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerMiddlewareService {
@@ -41,7 +42,9 @@ export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerM
const usersOut = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items
+ .filter((item) => item.headKind === ResourceKind.USER)
+ .map(item => item.headUuid))
.getFilters(),
count: "none"
});
@@ -49,7 +52,9 @@ export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerM
const collectionsOut = await this.services.collectionService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items
+ .filter((item) => item.headKind === ResourceKind.COLLECTION)
+ .map(item => item.headUuid))
.getFilters(),
count: "none"
});
@@ -57,7 +62,9 @@ export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerM
const projectsOut = await this.services.projectService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items
+ .filter((item) => item.headKind === ResourceKind.PROJECT)
+ .map(item => item.headUuid))
.getFilters(),
count: "none"
});
commit 418b900ceaaace5aa2e844959dee9c41d35fbe2d
Author: Stephen Smith <stephen at curii.com>
Date: Wed Oct 20 20:55:07 2021 -0400
18123: Fix add and remove group member.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts
index e71f75f3..b9ac0d79 100644
--- a/src/store/group-details-panel/group-details-panel-actions.ts
+++ b/src/store/group-details-panel/group-details-panel-actions.ts
@@ -16,7 +16,6 @@ import { RootState } from 'store/store';
import { ServiceRepository } from 'services/services';
import { PermissionResource } from 'models/permission';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
-import { UserResource, getUserDisplayName } from 'models/user';
export const GROUP_DETAILS_MEMBERS_PANEL_ID = 'groupDetailsMembersPanel';
export const GROUP_DETAILS_PERMISSIONS_PANEL_ID = 'groupDetailsPermissionsPanel';
@@ -108,20 +107,11 @@ export const removeGroupMember = (uuid: string) =>
const groupUuid = getCurrentGroupDetailsPanelUuid(getState().properties);
if (groupUuid) {
-
- const group = getResource<GroupResource>(groupUuid)(getState().resources);
- const user = getResource<UserResource>(groupUuid)(getState().resources);
-
dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
await deleteGroupMember({
- user: {
+ link: {
uuid,
- name: user ? getUserDisplayName(user) : uuid,
- },
- group: {
- uuid: groupUuid,
- name: group ? group.name : groupUuid,
},
permissionService,
dispatch,
diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts
index 0d92e946..dcf81f2d 100644
--- a/src/store/groups-panel/groups-panel-actions.ts
+++ b/src/store/groups-panel/groups-panel-actions.ts
@@ -113,8 +113,8 @@ interface AddGroupMemberArgs {
*/
export const addGroupMember = async ({ user, group, ...args }: AddGroupMemberArgs) => {
await createPermission({
- head: { ...user },
- tail: { ...group },
+ head: { ...group },
+ tail: { ...user },
permissionLevel: PermissionLevel.CAN_READ,
...args,
});
@@ -144,33 +144,29 @@ const createPermission = async ({ head, tail, permissionLevel, dispatch, permiss
};
interface DeleteGroupMemberArgs {
- user: { uuid: string, name: string };
- group: { uuid: string, name: string };
+ link: { uuid: string };
dispatch: Dispatch;
permissionService: PermissionService;
}
-export const deleteGroupMember = async ({ user, group, ...args }: DeleteGroupMemberArgs) => {
+export const deleteGroupMember = async ({ link, ...args }: DeleteGroupMemberArgs) => {
await deletePermission({
- tail: group,
- head: user,
+ uuid: link.uuid,
...args,
});
};
interface DeletePermissionLinkArgs {
- head: { uuid: string, name: string };
- tail: { uuid: string, name: string };
+ uuid: string;
dispatch: Dispatch;
permissionService: PermissionService;
}
-export const deletePermission = async ({ head, tail, dispatch, permissionService }: DeletePermissionLinkArgs) => {
+export const deletePermission = async ({ uuid, dispatch, permissionService }: DeletePermissionLinkArgs) => {
try {
const permissionsResponse = await permissionService.list({
filters: new FilterBuilder()
- .addEqual('tail_uuid', tail.uuid)
- .addEqual('head_uuid', head.uuid)
+ .addEqual('uuid', uuid)
.getFilters()
});
const [permission] = permissionsResponse.items;
@@ -181,8 +177,8 @@ export const deletePermission = async ({ head, tail, dispatch, permissionService
}
} catch (e) {
dispatch(snackbarActions.OPEN_SNACKBAR({
- message: `Could not delete ${tail.name} -> ${head.name} relation`,
+ message: `Could not delete ${uuid} permission`,
kind: SnackbarKind.ERROR,
}));
}
-};
\ No newline at end of file
+};
commit 2b6003f9be46bd178159a116886b02192e2ebfaa
Author: Stephen Smith <stephen at curii.com>
Date: Wed Oct 13 19:59:49 2021 -0400
18123: Add projects to group member list, make variables and error messages clearer.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
index 23f0bbfd..05a12d22 100644
--- a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
@@ -26,26 +26,34 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl
api.dispatch(groupsDetailsPanelDataExplorerIsNotSet());
} else {
try {
- const permissions = await this.services.permissionService.list({
+ const permissionsIn = await this.services.permissionService.list({
filters: new FilterBuilder()
.addEqual('head_uuid', groupUuid)
.addEqual('link_class', LinkClass.PERMISSION)
.getFilters()
});
- api.dispatch(updateResources(permissions.items));
+ api.dispatch(updateResources(permissionsIn.items));
- const users = await this.services.userService.list({
+ api.dispatch(GroupMembersPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(permissionsIn),
+ items: permissionsIn.items.map(item => item.uuid),
+ }));
+
+ const usersIn = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissions.items.map(item => item.tailUuid))
+ .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
.getFilters(),
count: "none"
});
- api.dispatch(updateResources(users.items));
+ api.dispatch(updateResources(usersIn.items));
- api.dispatch(GroupMembersPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissions),
- items: permissions.items.map(item => item.uuid),
- }));
+ const projectsIn = await this.services.projectService.list({
+ filters: new FilterBuilder()
+ .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
+ .getFilters(),
+ count: "none"
+ });
+ api.dispatch(updateResources(projectsIn.items));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
}
@@ -55,12 +63,12 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl
const groupsDetailsPanelDataExplorerIsNotSet = () =>
snackbarActions.OPEN_SNACKBAR({
- message: 'Group details panel is not ready.',
+ message: 'Group members panel is not ready.',
kind: SnackbarKind.ERROR
});
const couldNotFetchGroupDetailsContents = () =>
snackbarActions.OPEN_SNACKBAR({
- message: 'Could not fetch group details.',
+ message: 'Could not fetch group members.',
kind: SnackbarKind.ERROR
});
diff --git a/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
index 91bff1e9..7ce2d380 100644
--- a/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
@@ -26,42 +26,42 @@ export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerM
api.dispatch(groupsDetailsPanelDataExplorerIsNotSet());
} else {
try {
- const permissions = await this.services.permissionService.list({
+ const permissionsOut = await this.services.permissionService.list({
filters: new FilterBuilder()
.addEqual('tail_uuid', groupUuid)
.addEqual('link_class', LinkClass.PERMISSION)
.getFilters()
});
- api.dispatch(updateResources(permissions.items));
+ api.dispatch(updateResources(permissionsOut.items));
- const users = await this.services.userService.list({
+ api.dispatch(GroupPermissionsPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(permissionsOut),
+ items: permissionsOut.items.map(item => item.uuid),
+ }));
+
+ const usersOut = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissions.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
.getFilters(),
count: "none"
});
- api.dispatch(updateResources(users.items));
+ api.dispatch(updateResources(usersOut.items));
- const collections = await this.services.collectionService.list({
+ const collectionsOut = await this.services.collectionService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissions.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
.getFilters(),
count: "none"
});
- api.dispatch(updateResources(collections.items));
+ api.dispatch(updateResources(collectionsOut.items));
- const projects = await this.services.projectService.list({
+ const projectsOut = await this.services.projectService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissions.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsOut.items.map(item => item.headUuid))
.getFilters(),
count: "none"
});
- api.dispatch(updateResources(projects.items));
-
- api.dispatch(GroupPermissionsPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissions),
- items: permissions.items.map(item => item.uuid),
- }));
+ api.dispatch(updateResources(projectsOut.items));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
}
commit 8a6c5e3b1ac52cdd6e7e07f8349da92b31216c76
Author: Stephen Smith <stephen at curii.com>
Date: Wed Oct 13 17:07:27 2021 -0400
18123: List all permission links in group members list & cleanup.
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
index e295579a..23f0bbfd 100644
--- a/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
@@ -44,7 +44,7 @@ export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddl
api.dispatch(GroupMembersPanelActions.SET_ITEMS({
...listResultsToDataExplorerItemsMeta(permissions),
- items: users.items.map(item => item.uuid),
+ items: permissions.items.map(item => item.uuid),
}));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 99ca02d4..05ae4ce2 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -303,7 +303,7 @@ export const ResourceLinkClass = connect(
const renderLink = (dispatch: Dispatch, item: Resource) => {
var displayName = '';
- if ((item as UserResource).kind == ResourceKind.USER
+ if ((item as UserResource).kind === ResourceKind.USER
&& typeof (item as UserResource).firstName !== 'undefined') {
// We can be sure the resource is UserResource
displayName = getUserDisplayName(item as UserResource);
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index 3baff579..5b1a884e 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceFullName, ResourceUuid, ResourceEmail, ResourceUsername, ResourceLinkName, ResourceLinkHead, ResourceName } from 'views-components/data-explorer/renderers';
+import { ResourceUuid, ResourceEmail, ResourceUsername, ResourceLinkName, ResourceLinkHead, ResourceLinkTail } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
@@ -38,7 +38,7 @@ export const groupDetailsMembersPanelColumns: DataColumns<string> = [
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceFullName uuid={uuid} />
+ render: uuid => <ResourceLinkTail uuid={uuid} />
},
{
name: GroupDetailsPanelMembersColumnNames.USERNAME,
commit 55b1d7f984ac36dab63edc89e3b132b61fedbbfa
Author: Stephen Smith <stephen at curii.com>
Date: Wed Oct 13 13:56:10 2021 -0400
18123: Improve checking of group permission head type for permissions list
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 6766d4c0..99ca02d4 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -301,11 +301,18 @@ export const ResourceLinkClass = connect(
// };
const renderLink = (dispatch: Dispatch, item: Resource) => {
- const name = (item as LinkResource).name;
- const fullName = getUserDisplayName(item as UserResource);
+ var displayName = '';
+
+ if ((item as UserResource).kind == ResourceKind.USER
+ && typeof (item as UserResource).firstName !== 'undefined') {
+ // We can be sure the resource is UserResource
+ displayName = getUserDisplayName(item as UserResource);
+ } else {
+ displayName = (item as GroupContentsResource).name;
+ }
return <Typography noWrap color="primary" style={{ 'cursor': 'pointer' }} onClick={() => dispatch<any>(navigateTo(item.uuid))}>
- {resourceLabel(item.kind)}: {name || fullName || item.uuid}
+ {resourceLabel(item.kind)}: {displayName || item.uuid}
</Typography>;
}
commit cfa8c66b29ba7eb4cab946ab8bb2f58ca93bc80a
Author: Stephen Smith <stephen at curii.com>
Date: Wed Oct 13 08:34:24 2021 -0400
18123: Add group details permissions columns and tweaked renderers
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts
index 5a190a5f..e71f75f3 100644
--- a/src/store/group-details-panel/group-details-panel-actions.ts
+++ b/src/store/group-details-panel/group-details-panel-actions.ts
@@ -18,26 +18,26 @@ import { PermissionResource } from 'models/permission';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { UserResource, getUserDisplayName } from 'models/user';
-export const GROUP_DETAILS_PANEL_ID = 'groupMembersPanel';
-export const GROUP_PERMISSIONS_PANEL_ID = 'groupPermissionsPanel';
+export const GROUP_DETAILS_MEMBERS_PANEL_ID = 'groupDetailsMembersPanel';
+export const GROUP_DETAILS_PERMISSIONS_PANEL_ID = 'groupDetailsPermissionsPanel';
export const ADD_GROUP_MEMBERS_DIALOG = 'addGrupMembers';
export const ADD_GROUP_MEMBERS_FORM = 'addGrupMembers';
export const ADD_GROUP_MEMBERS_USERS_FIELD_NAME = 'users';
export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog';
export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog';
-export const GroupMembersPanelActions = bindDataExplorerActions(GROUP_DETAILS_PANEL_ID);
-export const GroupPermissionsPanelActions = bindDataExplorerActions(GROUP_PERMISSIONS_PANEL_ID);
+export const GroupMembersPanelActions = bindDataExplorerActions(GROUP_DETAILS_MEMBERS_PANEL_ID);
+export const GroupPermissionsPanelActions = bindDataExplorerActions(GROUP_DETAILS_PERMISSIONS_PANEL_ID);
export const loadGroupDetailsPanel = (groupUuid: string) =>
(dispatch: Dispatch) => {
- dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_DETAILS_PANEL_ID, value: groupUuid }));
+ dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_DETAILS_MEMBERS_PANEL_ID, value: groupUuid }));
dispatch(GroupMembersPanelActions.REQUEST_ITEMS());
- dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_PERMISSIONS_PANEL_ID, value: groupUuid }));
+ dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_DETAILS_PERMISSIONS_PANEL_ID, value: groupUuid }));
dispatch(GroupPermissionsPanelActions.REQUEST_ITEMS());
};
-export const getCurrentGroupDetailsPanelUuid = getProperty<string>(GROUP_DETAILS_PANEL_ID);
+export const getCurrentGroupDetailsPanelUuid = getProperty<string>(GROUP_DETAILS_MEMBERS_PANEL_ID);
export interface AddGroupMembersFormData {
[ADD_GROUP_MEMBERS_USERS_FIELD_NAME]: Participant[];
diff --git a/src/store/group-details-panel/group-details-panel-middleware-service.ts b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
similarity index 64%
copy from src/store/group-details-panel/group-details-panel-middleware-service.ts
copy to src/store/group-details-panel/group-details-panel-members-middleware-service.ts
index 67f72391..e295579a 100644
--- a/src/store/group-details-panel/group-details-panel-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-members-middleware-service.ts
@@ -10,10 +10,10 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getDataExplorer } from "store/data-explorer/data-explorer-reducer";
import { FilterBuilder } from 'services/api/filter-builder';
import { updateResources } from 'store/resources/resources-actions';
-import { getCurrentGroupDetailsPanelUuid, GroupMembersPanelActions, GroupPermissionsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
+import { getCurrentGroupDetailsPanelUuid, GroupMembersPanelActions } from 'store/group-details-panel/group-details-panel-actions';
import { LinkClass } from 'models/link';
-export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareService {
+export class GroupDetailsPanelMembersMiddlewareService extends DataExplorerMiddlewareService {
constructor(private services: ServiceRepository, id: string) {
super(id);
@@ -26,41 +26,26 @@ export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareSe
api.dispatch(groupsDetailsPanelDataExplorerIsNotSet());
} else {
try {
- const permissionsIn = await this.services.permissionService.list({
+ const permissions = await this.services.permissionService.list({
filters: new FilterBuilder()
.addEqual('head_uuid', groupUuid)
.addEqual('link_class', LinkClass.PERMISSION)
.getFilters()
});
- api.dispatch(updateResources(permissionsIn.items));
- const permissionsOut = await this.services.permissionService.list({
- filters: new FilterBuilder()
- .addEqual('tail_uuid', groupUuid)
- .addEqual('link_class', LinkClass.PERMISSION)
- .getFilters()
- });
- api.dispatch(updateResources(permissionsOut.items));
+ api.dispatch(updateResources(permissions.items));
+
const users = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
- .getFilters(),
- count: "none"
- });
- const usersOut = await this.services.userService.list({
- filters: new FilterBuilder()
- .addIn('uuid', permissionsOut.items.map(item => item.tailUuid))
+ .addIn('uuid', permissions.items.map(item => item.tailUuid))
.getFilters(),
count: "none"
});
+ api.dispatch(updateResources(users.items));
+
api.dispatch(GroupMembersPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissionsIn),
+ ...listResultsToDataExplorerItemsMeta(permissions),
items: users.items.map(item => item.uuid),
}));
- api.dispatch(GroupPermissionsPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissionsOut),
- items: usersOut.items.map(item => item.uuid),
- }));
- api.dispatch(updateResources(users.items));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
}
diff --git a/src/store/group-details-panel/group-details-panel-middleware-service.ts b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
similarity index 64%
rename from src/store/group-details-panel/group-details-panel-middleware-service.ts
rename to src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
index 67f72391..91bff1e9 100644
--- a/src/store/group-details-panel/group-details-panel-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-permissions-middleware-service.ts
@@ -10,10 +10,10 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getDataExplorer } from "store/data-explorer/data-explorer-reducer";
import { FilterBuilder } from 'services/api/filter-builder';
import { updateResources } from 'store/resources/resources-actions';
-import { getCurrentGroupDetailsPanelUuid, GroupMembersPanelActions, GroupPermissionsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
+import { getCurrentGroupDetailsPanelUuid, GroupPermissionsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
import { LinkClass } from 'models/link';
-export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareService {
+export class GroupDetailsPanelPermissionsMiddlewareService extends DataExplorerMiddlewareService {
constructor(private services: ServiceRepository, id: string) {
super(id);
@@ -26,41 +26,42 @@ export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareSe
api.dispatch(groupsDetailsPanelDataExplorerIsNotSet());
} else {
try {
- const permissionsIn = await this.services.permissionService.list({
- filters: new FilterBuilder()
- .addEqual('head_uuid', groupUuid)
- .addEqual('link_class', LinkClass.PERMISSION)
- .getFilters()
- });
- api.dispatch(updateResources(permissionsIn.items));
- const permissionsOut = await this.services.permissionService.list({
+ const permissions = await this.services.permissionService.list({
filters: new FilterBuilder()
.addEqual('tail_uuid', groupUuid)
.addEqual('link_class', LinkClass.PERMISSION)
.getFilters()
});
- api.dispatch(updateResources(permissionsOut.items));
+ api.dispatch(updateResources(permissions.items));
+
const users = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
+ .addIn('uuid', permissions.items.map(item => item.headUuid))
.getFilters(),
count: "none"
});
- const usersOut = await this.services.userService.list({
+ api.dispatch(updateResources(users.items));
+
+ const collections = await this.services.collectionService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissionsOut.items.map(item => item.tailUuid))
+ .addIn('uuid', permissions.items.map(item => item.headUuid))
.getFilters(),
count: "none"
});
- api.dispatch(GroupMembersPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissionsIn),
- items: users.items.map(item => item.uuid),
- }));
+ api.dispatch(updateResources(collections.items));
+
+ const projects = await this.services.projectService.list({
+ filters: new FilterBuilder()
+ .addIn('uuid', permissions.items.map(item => item.headUuid))
+ .getFilters(),
+ count: "none"
+ });
+ api.dispatch(updateResources(projects.items));
+
api.dispatch(GroupPermissionsPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissionsOut),
- items: usersOut.items.map(item => item.uuid),
+ ...listResultsToDataExplorerItemsMeta(permissions),
+ items: permissions.items.map(item => item.uuid),
}));
- api.dispatch(updateResources(users.items));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
}
@@ -70,12 +71,12 @@ export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareSe
const groupsDetailsPanelDataExplorerIsNotSet = () =>
snackbarActions.OPEN_SNACKBAR({
- message: 'Group details panel is not ready.',
+ message: 'Group permissions panel is not ready.',
kind: SnackbarKind.ERROR
});
const couldNotFetchGroupDetailsContents = () =>
snackbarActions.OPEN_SNACKBAR({
- message: 'Could not fetch group details.',
+ message: 'Could not fetch group permissions.',
kind: SnackbarKind.ERROR
});
diff --git a/src/store/store.ts b/src/store/store.ts
index 59a0cb12..688c8a05 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -51,8 +51,9 @@ import { UserMiddlewareService } from 'store/users/user-panel-middleware-service
import { USERS_PANEL_ID } from 'store/users/users-actions';
import { GroupsPanelMiddlewareService } from 'store/groups-panel/groups-panel-middleware-service';
import { GROUPS_PANEL_ID } from 'store/groups-panel/groups-panel-actions';
-import { GroupDetailsPanelMiddlewareService } from 'store/group-details-panel/group-details-panel-middleware-service';
-import { GROUP_DETAILS_PANEL_ID } from 'store/group-details-panel/group-details-panel-actions';
+import { GroupDetailsPanelMembersMiddlewareService } from 'store/group-details-panel/group-details-panel-members-middleware-service';
+import { GroupDetailsPanelPermissionsMiddlewareService } from 'store/group-details-panel/group-details-panel-permissions-middleware-service';
+import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID } from 'store/group-details-panel/group-details-panel-actions';
import { LINK_PANEL_ID } from 'store/link-panel/link-panel-actions';
import { LinkMiddlewareService } from 'store/link-panel/link-panel-middleware-service';
import { API_CLIENT_AUTHORIZATION_PANEL_ID } from 'store/api-client-authorizations/api-client-authorizations-actions';
@@ -116,8 +117,11 @@ export function configureStore(history: History, services: ServiceRepository, co
const groupsPanelMiddleware = dataExplorerMiddleware(
new GroupsPanelMiddlewareService(services, GROUPS_PANEL_ID)
);
- const groupDetailsPanelMiddleware = dataExplorerMiddleware(
- new GroupDetailsPanelMiddlewareService(services, GROUP_DETAILS_PANEL_ID)
+ const groupDetailsPanelMembersMiddleware = dataExplorerMiddleware(
+ new GroupDetailsPanelMembersMiddlewareService(services, GROUP_DETAILS_MEMBERS_PANEL_ID)
+ );
+ const groupDetailsPanelPermissionsMiddleware = dataExplorerMiddleware(
+ new GroupDetailsPanelPermissionsMiddlewareService(services, GROUP_DETAILS_PERMISSIONS_PANEL_ID)
);
const linkPanelMiddleware = dataExplorerMiddleware(
new LinkMiddlewareService(services, LINK_PANEL_ID)
@@ -157,7 +161,8 @@ export function configureStore(history: History, services: ServiceRepository, co
workflowPanelMiddleware,
userPanelMiddleware,
groupsPanelMiddleware,
- groupDetailsPanelMiddleware,
+ groupDetailsPanelMembersMiddleware,
+ groupDetailsPanelPermissionsMiddleware,
linkPanelMiddleware,
apiClientAuthorizationMiddlewareService,
publicFavoritesMiddleware,
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index 1ee0a9f8..9c89d199 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -88,7 +88,7 @@ import { apiClientAuthorizationPanelColumns } from 'views/api-client-authorizati
import * as groupPanelActions from 'store/groups-panel/groups-panel-actions';
import { groupsPanelColumns } from 'views/groups-panel/groups-panel';
import * as groupDetailsPanelActions from 'store/group-details-panel/group-details-panel-actions';
-import { groupDetailsPanelColumns } from 'views/group-details-panel/group-details-panel';
+import { groupDetailsMembersPanelColumns, groupDetailsPermissionsPanelColumns } from 'views/group-details-panel/group-details-panel';
import { DataTableFetchMode } from "components/data-table/data-table";
import { loadPublicFavoritePanel, publicFavoritePanelActions } from 'store/public-favorites-panel/public-favorites-action';
import { publicFavoritePanelColumns } from 'views/public-favorites-panel/public-favorites-panel';
@@ -136,8 +136,8 @@ export const loadWorkbench = () =>
dispatch(searchResultsPanelActions.SET_COLUMNS({ columns: searchResultsPanelColumns }));
dispatch(userBindedActions.SET_COLUMNS({ columns: userPanelColumns }));
dispatch(groupPanelActions.GroupsPanelActions.SET_COLUMNS({ columns: groupsPanelColumns }));
- dispatch(groupDetailsPanelActions.GroupMembersPanelActions.SET_COLUMNS({ columns: groupDetailsPanelColumns }));
- dispatch(groupDetailsPanelActions.GroupPermissionsPanelActions.SET_COLUMNS({ columns: groupDetailsPanelColumns }));
+ dispatch(groupDetailsPanelActions.GroupMembersPanelActions.SET_COLUMNS({ columns: groupDetailsMembersPanelColumns }));
+ dispatch(groupDetailsPanelActions.GroupPermissionsPanelActions.SET_COLUMNS({ columns: groupDetailsPermissionsPanelColumns }));
dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
dispatch(apiClientAuthorizationsActions.SET_COLUMNS({ columns: apiClientAuthorizationPanelColumns }));
dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: collectionContentAddressPanelColumns }));
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index ef8f70d4..6766d4c0 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -20,7 +20,7 @@ import { WorkflowResource } from 'models/workflow';
import { ResourceStatus as WorkflowStatus } from 'views/workflow-panel/workflow-panel-view';
import { getUuidPrefix, openRunProcess } from 'store/workflow-panel/workflow-panel-actions';
import { openSharingDialog } from 'store/sharing-dialog/sharing-dialog-actions';
-import { getUserFullname, User, UserResource } from 'models/user';
+import { getUserFullname, getUserDisplayName, User, UserResource } from 'models/user';
import { toggleIsActive, toggleIsAdmin } from 'store/users/users-actions';
import { LinkResource } from 'models/link';
import { navigateTo } from 'store/navigation/navigation-action';
@@ -286,45 +286,50 @@ export const ResourceLinkClass = connect(
return resource || { linkClass: '' };
})(renderLinkClass);
-const renderLinkTail = (dispatch: Dispatch, item: { uuid: string, tailUuid: string, tailKind: string }) => {
- const currentLabel = resourceLabel(item.tailKind);
- const isUnknow = currentLabel === "Unknown";
- return (<div>
- {!isUnknow ? (
- renderLink(dispatch, item.tailUuid, currentLabel)
- ) : (
- <Typography noWrap color="default">
- {item.tailUuid}
- </Typography>
- )}
- </div>);
-};
-
-const renderLink = (dispatch: Dispatch, uuid: string, label: string) =>
- <Typography noWrap color="primary" style={{ 'cursor': 'pointer' }} onClick={() => dispatch<any>(navigateTo(uuid))}>
- {label}: {uuid}
+// const renderLinkTail = (dispatch: Dispatch, item: { uuid: string, tailUuid: string, tailKind: string }) => {
+// const currentLabel = resourceLabel(item.tailKind);
+// const isUnknow = currentLabel === "Unknown";
+// return (<div>
+// {!isUnknow ? (
+// renderLink(dispatch, item.tailUuid, "name", currentLabel)
+// ) : (
+// <Typography noWrap color="default">
+// {item.tailUuid}
+// </Typography>
+// )}
+// </div>);
+// };
+
+const renderLink = (dispatch: Dispatch, item: Resource) => {
+ const name = (item as LinkResource).name;
+ const fullName = getUserDisplayName(item as UserResource);
+
+ return <Typography noWrap color="primary" style={{ 'cursor': 'pointer' }} onClick={() => dispatch<any>(navigateTo(item.uuid))}>
+ {resourceLabel(item.kind)}: {name || fullName || item.uuid}
</Typography>;
+}
export const ResourceLinkTail = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ const tailResource = getResource<Resource>(resource?.tailUuid || '')(state.resources);
+
return {
- item: resource || { uuid: '', tailUuid: '', tailKind: ResourceKind.NONE }
+ item: tailResource || { uuid: resource?.tailUuid || '', kind: resource?.headKind || ResourceKind.NONE }
};
- })((props: { item: any } & DispatchProp<any>) =>
- renderLinkTail(props.dispatch, props.item));
-
-const renderLinkHead = (dispatch: Dispatch, item: { uuid: string, headUuid: string, headKind: ResourceKind }) =>
- renderLink(dispatch, item.headUuid, resourceLabel(item.headKind));
+ })((props: { item: Resource } & DispatchProp<any>) =>
+ renderLink(props.dispatch, props.item));
export const ResourceLinkHead = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<LinkResource>(props.uuid)(state.resources);
+ const headResource = getResource<Resource>(resource?.headUuid || '')(state.resources);
+
return {
- item: resource || { uuid: '', headUuid: '', headKind: ResourceKind.NONE }
+ item: headResource || { uuid: resource?.headUuid || '', kind: resource?.headKind || ResourceKind.NONE }
};
- })((props: { item: any } & DispatchProp<any>) =>
- renderLinkHead(props.dispatch, props.item));
+ })((props: { item: Resource } & DispatchProp<any>) =>
+ renderLink(props.dispatch, props.item));
export const ResourceLinkUuid = connect(
(state: RootState, props: { uuid: string }) => {
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index 3885ec18..3baff579 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,11 +7,11 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceFullName, ResourceUuid, ResourceEmail, ResourceUsername } from 'views-components/data-explorer/renderers';
+import { ResourceFullName, ResourceUuid, ResourceEmail, ResourceUsername, ResourceLinkName, ResourceLinkHead, ResourceName } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
-import { GROUP_DETAILS_PANEL_ID, GROUP_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog } from 'store/group-details-panel/group-details-panel-actions';
+import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog } 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';
@@ -19,37 +19,43 @@ import { PermissionResource } from 'models/permission';
import { Grid, Button, Tabs, Tab, Paper } from '@material-ui/core';
import { AddIcon } from 'components/icon/icon';
-export enum GroupDetailsPanelColumnNames {
+export enum GroupDetailsPanelMembersColumnNames {
FULL_NAME = "Name",
UUID = "UUID",
EMAIL = "Email",
USERNAME = "Username",
}
-export const groupDetailsPanelColumns: DataColumns<string> = [
+export enum GroupDetailsPanelPermissionsColumnNames {
+ HEAD = "Head",
+ NAME = "Name",
+ UUID = "UUID",
+}
+
+export const groupDetailsMembersPanelColumns: DataColumns<string> = [
{
- name: GroupDetailsPanelColumnNames.FULL_NAME,
+ name: GroupDetailsPanelMembersColumnNames.FULL_NAME,
selected: true,
configurable: true,
filters: createTree(),
render: uuid => <ResourceFullName uuid={uuid} />
},
{
- name: GroupDetailsPanelColumnNames.USERNAME,
+ name: GroupDetailsPanelMembersColumnNames.USERNAME,
selected: true,
configurable: true,
filters: createTree(),
render: uuid => <ResourceUsername uuid={uuid} />
},
{
- name: GroupDetailsPanelColumnNames.UUID,
+ name: GroupDetailsPanelMembersColumnNames.UUID,
selected: true,
configurable: true,
filters: createTree(),
render: uuid => <ResourceUuid uuid={uuid} />
},
{
- name: GroupDetailsPanelColumnNames.EMAIL,
+ name: GroupDetailsPanelMembersColumnNames.EMAIL,
selected: true,
configurable: true,
filters: createTree(),
@@ -57,6 +63,30 @@ export const groupDetailsPanelColumns: DataColumns<string> = [
},
];
+export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
+ {
+ name: GroupDetailsPanelPermissionsColumnNames.HEAD,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkHead uuid={uuid} />
+ },
+ {
+ name: GroupDetailsPanelPermissionsColumnNames.NAME,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceLinkName uuid={uuid} />
+ },
+ {
+ name: GroupDetailsPanelPermissionsColumnNames.UUID,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceUuid uuid={uuid} />
+ },
+];
+
const mapStateToProps = (state: RootState) => {
return {
resources: state.resources
@@ -90,13 +120,13 @@ export const GroupDetailsPanel = connect(
const { value } = this.state;
return (
<Paper>
- <Tabs value={value} onChange={this.handleChange} fullWidth>
+ <Tabs value={value} onChange={this.handleChange} variant="fullWidth">
<Tab label="MEMBERS" />
<Tab label="PERMISSIONS" />
</Tabs>
{value === 0 &&
<DataExplorer
- id={GROUP_DETAILS_PANEL_ID}
+ id={GROUP_DETAILS_MEMBERS_PANEL_ID}
onRowClick={noop}
onRowDoubleClick={noop}
onContextMenu={this.handleContextMenu}
@@ -119,7 +149,7 @@ export const GroupDetailsPanel = connect(
}
{value === 1 &&
<DataExplorer
- id={GROUP_PERMISSIONS_PANEL_ID}
+ id={GROUP_DETAILS_PERMISSIONS_PANEL_ID}
onRowClick={noop}
onRowDoubleClick={noop}
onContextMenu={this.handleContextMenu}
commit 237776fd3412409bf0a9c9f0ac538f82d4b4a8d4
Author: Stephen Smith <stephen at curii.com>
Date: Thu Oct 7 00:19:11 2021 -0400
18123: Add permissions data explorer and tab in groups edit
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/group-details-panel/group-details-panel-actions.ts b/src/store/group-details-panel/group-details-panel-actions.ts
index 01e6c151..5a190a5f 100644
--- a/src/store/group-details-panel/group-details-panel-actions.ts
+++ b/src/store/group-details-panel/group-details-panel-actions.ts
@@ -18,19 +18,23 @@ import { PermissionResource } from 'models/permission';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { UserResource, getUserDisplayName } from 'models/user';
-export const GROUP_DETAILS_PANEL_ID = 'groupDetailsPanel';
+export const GROUP_DETAILS_PANEL_ID = 'groupMembersPanel';
+export const GROUP_PERMISSIONS_PANEL_ID = 'groupPermissionsPanel';
export const ADD_GROUP_MEMBERS_DIALOG = 'addGrupMembers';
export const ADD_GROUP_MEMBERS_FORM = 'addGrupMembers';
export const ADD_GROUP_MEMBERS_USERS_FIELD_NAME = 'users';
export const MEMBER_ATTRIBUTES_DIALOG = 'memberAttributesDialog';
export const MEMBER_REMOVE_DIALOG = 'memberRemoveDialog';
-export const GroupDetailsPanelActions = bindDataExplorerActions(GROUP_DETAILS_PANEL_ID);
+export const GroupMembersPanelActions = bindDataExplorerActions(GROUP_DETAILS_PANEL_ID);
+export const GroupPermissionsPanelActions = bindDataExplorerActions(GROUP_PERMISSIONS_PANEL_ID);
export const loadGroupDetailsPanel = (groupUuid: string) =>
(dispatch: Dispatch) => {
dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_DETAILS_PANEL_ID, value: groupUuid }));
- dispatch(GroupDetailsPanelActions.REQUEST_ITEMS());
+ dispatch(GroupMembersPanelActions.REQUEST_ITEMS());
+ dispatch(propertiesActions.SET_PROPERTY({ key: GROUP_PERMISSIONS_PANEL_ID, value: groupUuid }));
+ dispatch(GroupPermissionsPanelActions.REQUEST_ITEMS());
};
export const getCurrentGroupDetailsPanelUuid = getProperty<string>(GROUP_DETAILS_PANEL_ID);
@@ -72,7 +76,7 @@ export const addGroupMembers = ({ users }: AddGroupMembersFormData) =>
}
dispatch(dialogActions.CLOSE_DIALOG({ id: ADD_GROUP_MEMBERS_FORM }));
- dispatch(GroupDetailsPanelActions.REQUEST_ITEMS());
+ dispatch(GroupMembersPanelActions.REQUEST_ITEMS());
}
};
@@ -124,7 +128,7 @@ export const removeGroupMember = (uuid: string) =>
});
dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
- dispatch(GroupDetailsPanelActions.REQUEST_ITEMS());
+ dispatch(GroupMembersPanelActions.REQUEST_ITEMS());
}
diff --git a/src/store/group-details-panel/group-details-panel-middleware-service.ts b/src/store/group-details-panel/group-details-panel-middleware-service.ts
index 834b4c21..67f72391 100644
--- a/src/store/group-details-panel/group-details-panel-middleware-service.ts
+++ b/src/store/group-details-panel/group-details-panel-middleware-service.ts
@@ -10,7 +10,7 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getDataExplorer } from "store/data-explorer/data-explorer-reducer";
import { FilterBuilder } from 'services/api/filter-builder';
import { updateResources } from 'store/resources/resources-actions';
-import { getCurrentGroupDetailsPanelUuid, GroupDetailsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
+import { getCurrentGroupDetailsPanelUuid, GroupMembersPanelActions, GroupPermissionsPanelActions } from 'store/group-details-panel/group-details-panel-actions';
import { LinkClass } from 'models/link';
export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareService {
@@ -26,23 +26,40 @@ export class GroupDetailsPanelMiddlewareService extends DataExplorerMiddlewareSe
api.dispatch(groupsDetailsPanelDataExplorerIsNotSet());
} else {
try {
- const permissions = await this.services.permissionService.list({
+ const permissionsIn = await this.services.permissionService.list({
+ filters: new FilterBuilder()
+ .addEqual('head_uuid', groupUuid)
+ .addEqual('link_class', LinkClass.PERMISSION)
+ .getFilters()
+ });
+ api.dispatch(updateResources(permissionsIn.items));
+ const permissionsOut = await this.services.permissionService.list({
filters: new FilterBuilder()
.addEqual('tail_uuid', groupUuid)
.addEqual('link_class', LinkClass.PERMISSION)
.getFilters()
});
- api.dispatch(updateResources(permissions.items));
+ api.dispatch(updateResources(permissionsOut.items));
const users = await this.services.userService.list({
filters: new FilterBuilder()
- .addIn('uuid', permissions.items.map(item => item.headUuid))
+ .addIn('uuid', permissionsIn.items.map(item => item.tailUuid))
.getFilters(),
count: "none"
});
- api.dispatch(GroupDetailsPanelActions.SET_ITEMS({
- ...listResultsToDataExplorerItemsMeta(permissions),
+ const usersOut = await this.services.userService.list({
+ filters: new FilterBuilder()
+ .addIn('uuid', permissionsOut.items.map(item => item.tailUuid))
+ .getFilters(),
+ count: "none"
+ });
+ api.dispatch(GroupMembersPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(permissionsIn),
items: users.items.map(item => item.uuid),
}));
+ api.dispatch(GroupPermissionsPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(permissionsOut),
+ items: usersOut.items.map(item => item.uuid),
+ }));
api.dispatch(updateResources(users.items));
} catch (e) {
api.dispatch(couldNotFetchGroupDetailsContents());
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index 6ea30855..1ee0a9f8 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -136,7 +136,8 @@ export const loadWorkbench = () =>
dispatch(searchResultsPanelActions.SET_COLUMNS({ columns: searchResultsPanelColumns }));
dispatch(userBindedActions.SET_COLUMNS({ columns: userPanelColumns }));
dispatch(groupPanelActions.GroupsPanelActions.SET_COLUMNS({ columns: groupsPanelColumns }));
- dispatch(groupDetailsPanelActions.GroupDetailsPanelActions.SET_COLUMNS({ columns: groupDetailsPanelColumns }));
+ dispatch(groupDetailsPanelActions.GroupMembersPanelActions.SET_COLUMNS({ columns: groupDetailsPanelColumns }));
+ dispatch(groupDetailsPanelActions.GroupPermissionsPanelActions.SET_COLUMNS({ columns: groupDetailsPanelColumns }));
dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
dispatch(apiClientAuthorizationsActions.SET_COLUMNS({ columns: apiClientAuthorizationPanelColumns }));
dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: collectionContentAddressPanelColumns }));
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index faf7b3ae..3885ec18 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -11,7 +11,7 @@ import { ResourceFullName, ResourceUuid, ResourceEmail, ResourceUsername } from
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
-import { GROUP_DETAILS_PANEL_ID, openAddGroupMembersDialog } from 'store/group-details-panel/group-details-panel-actions';
+import { GROUP_DETAILS_PANEL_ID, GROUP_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog } 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';
@@ -78,27 +78,69 @@ export const GroupDetailsPanel = connect(
mapStateToProps, mapDispatchToProps
)(
class GroupDetailsPanel extends React.Component<GroupDetailsPanelProps> {
+ state = {
+ value: 0,
+ };
+
+ componentDidMount() {
+ this.setState({ value: 0 });
+ }
render() {
+ const { value } = this.state;
return (
- <DataExplorer
- id={GROUP_DETAILS_PANEL_ID}
- onRowClick={noop}
- onRowDoubleClick={noop}
- onContextMenu={this.handleContextMenu}
- contextMenuColumn={true}
- hideColumnSelector
- hideSearchInput
- actions={
- <Grid container justify='flex-end'>
- <Button
- variant="contained"
- color="primary"
- onClick={this.props.onAddUser}>
- <AddIcon /> Add user
- </Button>
- </Grid>
- } />
+ <Paper>
+ <Tabs value={value} onChange={this.handleChange} fullWidth>
+ <Tab label="MEMBERS" />
+ <Tab label="PERMISSIONS" />
+ </Tabs>
+ {value === 0 &&
+ <DataExplorer
+ id={GROUP_DETAILS_PANEL_ID}
+ onRowClick={noop}
+ onRowDoubleClick={noop}
+ onContextMenu={this.handleContextMenu}
+ contextMenuColumn={true}
+ hideColumnSelector
+ hideSearchInput
+ actions={
+ <Grid container justify='flex-end'>
+ <Button
+ variant="contained"
+ color="primary"
+ onClick={this.props.onAddUser}>
+ <AddIcon /> Add user
+ </Button>
+ </Grid>
+ }
+ paperProps={{
+ elevation: 0,
+ }} />
+ }
+ {value === 1 &&
+ <DataExplorer
+ id={GROUP_PERMISSIONS_PANEL_ID}
+ onRowClick={noop}
+ onRowDoubleClick={noop}
+ onContextMenu={this.handleContextMenu}
+ contextMenuColumn={true}
+ hideColumnSelector
+ hideSearchInput
+ actions={
+ <Grid container justify='flex-end'>
+ <Button
+ variant="contained"
+ color="primary"
+ onClick={this.props.onAddUser}>
+ <AddIcon /> Add user
+ </Button>
+ </Grid>
+ }
+ paperProps={{
+ elevation: 0,
+ }} />
+ }
+ </Paper>
);
}
@@ -114,5 +156,8 @@ export const GroupDetailsPanel = connect(
});
}
}
- });
+ handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
+ this.setState({ value });
+ }
+ });
commit de544885631caf7d6e6c1d9ae43763b4d612dfca
Author: Stephen Smith <stephen at curii.com>
Date: Thu Oct 7 00:17:53 2021 -0400
18123: Display full name of group members in group edit
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/views-components/data-explorer/renderers.tsx b/src/views-components/data-explorer/renderers.tsx
index 3965e69d..ef8f70d4 100644
--- a/src/views-components/data-explorer/renderers.tsx
+++ b/src/views-components/data-explorer/renderers.tsx
@@ -131,11 +131,11 @@ export const ResourceShare = connect(
})((props: { ownerUuid?: string, uuidPrefix: string, uuid?: string } & DispatchProp<any>) =>
resourceShare(props.dispatch, props.uuidPrefix, props.ownerUuid, props.uuid));
+// User Resources
const renderFirstName = (item: { firstName: string }) => {
return <Typography noWrap>{item.firstName}</Typography>;
};
-// User Resources
export const ResourceFirstName = connect(
(state: RootState, props: { uuid: string }) => {
const resource = getResource<UserResource>(props.uuid)(state.resources);
@@ -151,6 +151,16 @@ export const ResourceLastName = connect(
return resource || { lastName: '' };
})(renderLastName);
+const renderFullName = (item: { firstName: string, lastName: string }) =>
+ <Typography noWrap>{(item.firstName + " " + item.lastName).trim()}</Typography>;
+
+export const ResourceFullName = connect(
+ (state: RootState, props: { uuid: string }) => {
+ const resource = getResource<UserResource>(props.uuid)(state.resources);
+ return resource || { firstName: '', lastName: '' };
+ })(renderFullName);
+
+
const renderUuid = (item: { uuid: string }) =>
<Typography noWrap>{item.uuid}</Typography>;
diff --git a/src/views/group-details-panel/group-details-panel.tsx b/src/views/group-details-panel/group-details-panel.tsx
index d0f79736..faf7b3ae 100644
--- a/src/views/group-details-panel/group-details-panel.tsx
+++ b/src/views/group-details-panel/group-details-panel.tsx
@@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { DataExplorer } from "views-components/data-explorer/data-explorer";
import { DataColumns } from 'components/data-table/data-table';
-import { ResourceUuid, ResourceFirstName, ResourceLastName, ResourceEmail, ResourceUsername } from 'views-components/data-explorer/renderers';
+import { ResourceFullName, ResourceUuid, ResourceEmail, ResourceUsername } from 'views-components/data-explorer/renderers';
import { createTree } from 'models/tree';
import { noop } from 'lodash/fp';
import { RootState } from 'store/store';
@@ -16,12 +16,11 @@ 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';
-import { Grid, Button } from '@material-ui/core';
+import { Grid, Button, Tabs, Tab, Paper } from '@material-ui/core';
import { AddIcon } from 'components/icon/icon';
export enum GroupDetailsPanelColumnNames {
- FIRST_NAME = "First name",
- LAST_NAME = "Last name",
+ FULL_NAME = "Name",
UUID = "UUID",
EMAIL = "Email",
USERNAME = "Username",
@@ -29,18 +28,18 @@ export enum GroupDetailsPanelColumnNames {
export const groupDetailsPanelColumns: DataColumns<string> = [
{
- name: GroupDetailsPanelColumnNames.FIRST_NAME,
+ name: GroupDetailsPanelColumnNames.FULL_NAME,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceFirstName uuid={uuid} />
+ render: uuid => <ResourceFullName uuid={uuid} />
},
{
- name: GroupDetailsPanelColumnNames.LAST_NAME,
+ name: GroupDetailsPanelColumnNames.USERNAME,
selected: true,
configurable: true,
filters: createTree(),
- render: uuid => <ResourceLastName uuid={uuid} />
+ render: uuid => <ResourceUsername uuid={uuid} />
},
{
name: GroupDetailsPanelColumnNames.UUID,
@@ -56,13 +55,6 @@ export const groupDetailsPanelColumns: DataColumns<string> = [
filters: createTree(),
render: uuid => <ResourceEmail uuid={uuid} />
},
- {
- name: GroupDetailsPanelColumnNames.USERNAME,
- selected: true,
- configurable: true,
- filters: createTree(),
- render: uuid => <ResourceUsername uuid={uuid} />
- },
];
const mapStateToProps = (state: RootState) => {
commit b2485c13b72d66f5e207814a368b72334970afca
Author: Stephen Smith <stephen at curii.com>
Date: Thu Oct 7 00:15:48 2021 -0400
18123: Add Groups page to sidebar
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index 97082e5a..19cc36ae 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -9,7 +9,6 @@ import { SidePanelTreeCategory } from '../side-panel-tree/side-panel-tree-action
import { Routes, getProcessLogUrl, getGroupUrl, getNavUrl } from 'routes/routes';
import { RootState } from 'store/store';
import { ServiceRepository } from 'services/services';
-import { GROUPS_PANEL_LABEL } from 'store/breadcrumbs/breadcrumbs-actions';
import { pluginConfig } from 'plugins';
import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
@@ -64,7 +63,7 @@ export const navigateTo = (uuid: string) =>
case SidePanelTreeCategory.TRASH:
dispatch(navigateToTrash);
return;
- case GROUPS_PANEL_LABEL:
+ case SidePanelTreeCategory.GROUPS:
dispatch(navigateToGroups);
return;
case SidePanelTreeCategory.ALL_PROCESSES:
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index 66521f35..895fe79c 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -26,7 +26,8 @@ export enum SidePanelTreeCategory {
WORKFLOWS = 'Workflows',
FAVORITES = 'My Favorites',
TRASH = 'Trash',
- ALL_PROCESSES = 'All Processes'
+ ALL_PROCESSES = 'All Processes',
+ GROUPS = 'Groups',
}
export const SIDE_PANEL_TREE = 'sidePanelTree';
@@ -52,6 +53,7 @@ let SIDE_PANEL_CATEGORIES: string[] = [
SidePanelTreeCategory.PUBLIC_FAVORITES,
SidePanelTreeCategory.FAVORITES,
SidePanelTreeCategory.WORKFLOWS,
+ SidePanelTreeCategory.GROUPS,
SidePanelTreeCategory.ALL_PROCESSES,
SidePanelTreeCategory.TRASH
];
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list