--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { bindDataExplorerActions } from "~/store/data-explorer/data-explorer-action";
+
+export const GROUPS_PANEL_ID = "groupsPanel";
+export const GroupsPanelActions = bindDataExplorerActions(GROUPS_PANEL_ID);
+
+export const loadGroupsPanel = () => GroupsPanelActions.REQUEST_ITEMS();
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch, MiddlewareAPI } from "redux";
+import { DataExplorerMiddlewareService, listResultsToDataExplorerItemsMeta, dataExplorerToListParams } from "~/store/data-explorer/data-explorer-middleware-service";
+import { RootState } from "~/store/store";
+import { ServiceRepository } from "~/services/services";
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { getDataExplorer } from "~/store/data-explorer/data-explorer-reducer";
+import { GroupsPanelActions } from '~/store/groups-panel/groups-panel-actions';
+import { FilterBuilder } from '~/services/api/filter-builder';
+import { updateResources } from '~/store/resources/resources-actions';
+
+export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService {
+
+ constructor(private services: ServiceRepository, id: string) {
+ super(id);
+ }
+
+ async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+
+ const dataExplorer = getDataExplorer(api.getState().dataExplorer, this.getId());
+
+ if (!dataExplorer) {
+
+ api.dispatch(groupsPanelDataExplorerIsNotSet());
+
+ } else {
+
+ try {
+
+ const filters = new FilterBuilder()
+ .addEqual('groupClass', null)
+ .getFilters();
+
+ const response = await this.services.groupsService
+ .list({
+ ...dataExplorerToListParams(dataExplorer),
+ filters,
+ });
+
+ api.dispatch(updateResources(response.items));
+
+ api.dispatch(GroupsPanelActions.SET_ITEMS({
+ ...listResultsToDataExplorerItemsMeta(response),
+ items: response.items.map(item => item.uuid),
+ }));
+
+
+ } catch (e) {
+
+ api.dispatch(couldNotFetchFavoritesContents());
+
+ }
+ }
+ }
+}
+
+const groupsPanelDataExplorerIsNotSet = () =>
+ snackbarActions.OPEN_SNACKBAR({
+ message: 'Groups panel is not ready.'
+ });
+
+const couldNotFetchFavoritesContents = () =>
+ snackbarActions.OPEN_SNACKBAR({
+ message: 'Could not fetch groups.',
+ kind: SnackbarKind.ERROR
+ });
import { USERS_PANEL_ID } from '~/store/users/users-actions';
import { computeNodesReducer } from '~/store/compute-nodes/compute-nodes-reducer';
import { apiClientAuthorizationsReducer } from '~/store/api-client-authorizations/api-client-authorizations-reducer';
+import { GroupsPanelMiddlewareService } from '~/store/groups-panel/groups-panel-middleware-service';
+import { GROUPS_PANEL_ID } from '~/store/groups-panel/groups-panel-actions';
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
const userPanelMiddleware = dataExplorerMiddleware(
new UserMiddlewareService(services, USERS_PANEL_ID)
);
+ const groupsPanelMiddleware = dataExplorerMiddleware(
+ new GroupsPanelMiddlewareService(services, GROUPS_PANEL_ID)
+ );
const middlewares: Middleware[] = [
routerMiddleware(history),
searchResultsPanelMiddleware,
sharedWithMePanelMiddleware,
workflowPanelMiddleware,
- userPanelMiddleware
+ userPanelMiddleware,
+ groupsPanelMiddleware,
];
const enhancer = composeEnhancers(applyMiddleware(...middlewares));
return createStore(rootReducer, enhancer);
import { userPanelColumns } from '~/views/user-panel/user-panel';
import { loadComputeNodesPanel } from '~/store/compute-nodes/compute-nodes-actions';
import { loadApiClientAuthorizationsPanel } from '~/store/api-client-authorizations/api-client-authorizations-actions';
+import * as groupPanelActions from '~/store/groups-panel/groups-panel-actions';
+import { groupsPanelColumns } from '~/views/groups-panel/groups-panel';
export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
dispatch(workflowPanelActions.SET_COLUMNS({ columns: workflowPanelColumns }));
dispatch(searchResultsPanelActions.SET_COLUMNS({ columns: searchResultsPanelColumns }));
dispatch(userBindedActions.SET_COLUMNS({ columns: userPanelColumns }));
+ dispatch(groupPanelActions.GroupsPanelActions.SET_COLUMNS({ columns: groupsPanelColumns }));
dispatch<any>(initSidePanelTree());
if (router.location) {
const match = matchRootRoute(router.location.pathname);
export const loadGroupsPanel = handleFirstTimeLoad(
(dispatch: Dispatch<any>) => {
dispatch(setBreadcrumbs([{ label: 'Groups' }]));
+ dispatch(groupPanelActions.loadGroupsPanel());
});
const finishLoadingProject = (project: GroupContentsResource | string) =>
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import withStyles from "@material-ui/core/styles/withStyles";
+import { DispatchProp, connect } from 'react-redux';
+import { RouteComponentProps } from 'react-router';
+import { StyleRulesCallback, WithStyles, Typography } from "@material-ui/core";
+
+import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
+import { DataColumns } from '~/components/data-table/data-table';
+import { RootState } from '~/store/store';
+import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
+import { ContainerRequestState } from '~/models/container-request';
+import { SortDirection } from '~/components/data-table/data-column';
+import { ResourceKind, Resource } from '~/models/resource';
+import { ResourceFileSize, ResourceLastModifiedDate, ProcessStatus, ResourceType, ResourceOwner } from '~/views-components/data-explorer/renderers';
+import { ProjectIcon } from '~/components/icon/icon';
+import { ResourceName } from '~/views-components/data-explorer/renderers';
+import { ResourcesState, getResource } from '~/store/resources/resources';
+import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
+import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { ProjectResource } from '~/models/project';
+import { navigateTo } from '~/store/navigation/navigation-action';
+import { getProperty } from '~/store/properties/properties';
+import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { ArvadosTheme } from "~/common/custom-theme";
+import { createTree } from '~/models/tree';
+import { getInitialResourceTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
+import { GROUPS_PANEL_ID } from '~/store/groups-panel/groups-panel-actions';
+import { noop } from 'lodash/fp';
+import { GroupResource } from '~/models/group';
+
+export enum ProjectPanelColumnNames {
+ GROUP = "Name",
+ OWNER = "Owner",
+ MEMBERS = "Members",
+}
+
+export const groupsPanelColumns: DataColumns<string> = [
+ {
+ name: ProjectPanelColumnNames.GROUP,
+ selected: true,
+ configurable: true,
+ sortDirection: SortDirection.ASC,
+ filters: createTree(),
+ render: uuid => <ResourceName uuid={uuid} />
+ },
+ {
+ name: ProjectPanelColumnNames.OWNER,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <ResourceOwner uuid={uuid} />,
+ },
+ {
+ name: ProjectPanelColumnNames.MEMBERS,
+ selected: true,
+ configurable: true,
+ filters: createTree(),
+ render: uuid => <span>0</span>,
+ },
+];
+
+export class GroupsPanel extends React.Component {
+
+ render() {
+ return (
+ <DataExplorer
+ id={GROUPS_PANEL_ID}
+ onRowClick={noop}
+ onRowDoubleClick={noop}
+ onContextMenu={noop}
+ contextMenuColumn={true} />
+ );
+ }
+}
import { UserAttributesDialog } from '~/views-components/user-dialog/attributes-dialog';
import { CreateUserDialog } from '~/views-components/dialog-forms/create-user-dialog';
import { HelpApiClientAuthorizationDialog } from '~/views-components/api-client-authorizations-dialog/help-dialog';
+import { GroupsPanel } from '~/views/groups-panel/groups-panel';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
<Route path={Routes.COMPUTE_NODES} component={ComputeNodePanel} />
<Route path={Routes.API_CLIENT_AUTHORIZATIONS} component={ApiClientAuthorizationPanel} />
<Route path={Routes.MY_ACCOUNT} component={MyAccountPanel} />
- <Route path={Routes.GROUPS} component={() => <h1>Groups panel</h1>} />
+ <Route path={Routes.GROUPS} component={GroupsPanel} />
</Switch>
</Grid>
</Grid>