+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { unionize, ofType, UnionOf } from '~/common/unionize';
-import { ProjectResource } from "~/models/project";
-import { Dispatch } from "redux";
-import { FilterBuilder } from "~/common/api/filter-builder";
-import { RootState } from "../store";
-import { updateFavorites } from "../favorites/favorites-actions";
-import { ServiceRepository } from "~/services/services";
-import { resourcesActions } from '~/store/resources/resources-actions';
-
-export const projectActions = unionize({
- REMOVE_PROJECT: ofType<string>(),
- PROJECTS_REQUEST: ofType<string>(),
- PROJECTS_SUCCESS: ofType<{ projects: ProjectResource[], parentItemId?: string }>(),
- TOGGLE_PROJECT_TREE_ITEM_OPEN: ofType<string>(),
- TOGGLE_PROJECT_TREE_ITEM_ACTIVE: ofType<string>(),
- RESET_PROJECT_TREE_ACTIVITY: ofType<string>()
-});
-
-export const getProjectList = (parentUuid: string = '') =>
- (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(projectActions.PROJECTS_REQUEST(parentUuid));
- return services.projectService.list({
- filters: new FilterBuilder()
- .addEqual("ownerUuid", parentUuid)
- .getFilters()
- }).then(({ items: projects }) => {
- dispatch(projectActions.PROJECTS_SUCCESS({ projects, parentItemId: parentUuid }));
- dispatch<any>(updateFavorites(projects.map(project => project.uuid)));
- dispatch<any>(resourcesActions.SET_RESOURCES(projects));
- return projects;
- });
- };
-
-export type ProjectAction = UnionOf<typeof projectActions>;
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { projectsReducer, getTreePath } from "./project-reducer";
-import { projectActions } from "./project-action";
-import { TreeItem, TreeItemStatus } from "~/components/tree/tree";
-import { mockProjectResource } from "~/models/test-utils";
-
-describe('project-reducer', () => {
-
- it('should load projects', () => {
- const initialState = undefined;
-
- const projects = [mockProjectResource({ uuid: "1" }), mockProjectResource({ uuid: "2" })];
- const state = projectsReducer(initialState, projectActions.PROJECTS_SUCCESS({ projects, parentItemId: undefined }));
- expect(state).toEqual({
- items: [{
- active: false,
- open: false,
- id: "1",
- items: [],
- data: mockProjectResource({ uuid: "1" }),
- status: TreeItemStatus.INITIAL
- }, {
- active: false,
- open: false,
- id: "2",
- items: [],
- data: mockProjectResource({ uuid: "2" }),
- status: TreeItemStatus.INITIAL
- }
- ],
- currentItemId: ""
- });
- });
-
- it('should remove activity on projects list', () => {
- const initialState = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: true,
- active: true,
- status: TreeItemStatus.PENDING
- }],
- currentItemId: "1"
- };
- const project = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: true,
- active: false,
- status: TreeItemStatus.PENDING
- }],
- currentItemId: ""
- };
-
- const state = projectsReducer(initialState, projectActions.RESET_PROJECT_TREE_ACTIVITY(initialState.items[0].id));
- expect(state).toEqual(project);
- });
-
- it('should toggle project tree item activity', () => {
- const initialState = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: true,
- active: false,
- status: TreeItemStatus.PENDING
- }],
- currentItemId: "1"
- };
- const project = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: true,
- active: true,
- status: TreeItemStatus.PENDING,
- }],
- currentItemId: "1"
- };
-
- const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(initialState.items[0].id));
- expect(state).toEqual(project);
- });
-
-
- it('should close project tree item ', () => {
- const initialState = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: true,
- active: false,
- status: TreeItemStatus.PENDING,
- }],
- currentItemId: "1"
- };
- const project = {
- items: [{
- data: mockProjectResource(),
- id: "1",
- open: false,
- active: false,
- status: TreeItemStatus.PENDING,
- }],
- currentItemId: "1"
- };
-
- const state = projectsReducer(initialState, projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(initialState.items[0].id));
- expect(state).toEqual(project);
- });
-});
-
-describe("findTreeBranch", () => {
- const createTreeItem = (id: string, items?: Array<TreeItem<string>>): TreeItem<string> => ({
- id,
- items,
- active: false,
- data: "",
- open: false,
- status: TreeItemStatus.INITIAL
- });
-
- it("should return an array that matches path to the given item", () => {
- const tree: Array<TreeItem<string>> = [
- createTreeItem("1", [
- createTreeItem("1.1", [
- createTreeItem("1.1.1"),
- createTreeItem("1.1.2")
- ])
- ]),
- createTreeItem("2", [
- createTreeItem("2.1", [
- createTreeItem("2.1.1"),
- createTreeItem("2.1.2")
- ])
- ])
- ];
- const branch = getTreePath(tree, "2.1.1");
- expect(branch.map(item => item.id)).toEqual(["2", "2.1", "2.1.1"]);
- });
-
- it("should return empty array if item is not found", () => {
- const tree: Array<TreeItem<string>> = [
- createTreeItem("1", [
- createTreeItem("1.1", [
- createTreeItem("1.1.1"),
- createTreeItem("1.1.2")
- ])
- ]),
- createTreeItem("2", [
- createTreeItem("2.1", [
- createTreeItem("2.1.1"),
- createTreeItem("2.1.2")
- ])
- ])
- ];
- expect(getTreePath(tree, "3")).toHaveLength(0);
- });
-
-});
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as _ from "lodash";
-
-import { projectActions, ProjectAction } from "./project-action";
-import { TreeItem, TreeItemStatus } from "~/components/tree/tree";
-import { ProjectResource } from "~/models/project";
-
-export type ProjectState = {
- items: Array<TreeItem<ProjectResource>>,
- currentItemId: string
-};
-
-interface ProjectUpdater {
- opened: boolean;
- uuid: string;
-}
-
-export function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
- let item;
- for (const t of tree) {
- item = t.id === itemId
- ? t
- : findTreeItem(t.items ? t.items : [], itemId);
- if (item) {
- break;
- }
- }
- return item;
-}
-
-export function getActiveTreeItem<T>(tree: Array<TreeItem<T>>): TreeItem<T> | undefined {
- let item;
- for (const t of tree) {
- item = t.active
- ? t
- : getActiveTreeItem(t.items ? t.items : []);
- if (item) {
- break;
- }
- }
- return item;
-}
-
-export function getTreePath<T>(tree: Array<TreeItem<T>>, itemId: string): Array<TreeItem<T>> {
- for (const item of tree) {
- if (item.id === itemId) {
- return [item];
- } else {
- const branch = getTreePath(item.items || [], itemId);
- if (branch.length > 0) {
- return [item, ...branch];
- }
- }
- }
- return [];
-}
-
-function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
- for (const t of tree) {
- t.active = false;
- resetTreeActivity(t.items ? t.items : []);
- }
-}
-
-function updateProjectTree(tree: Array<TreeItem<ProjectResource>>, projects: ProjectResource[], parentItemId?: string): Array<TreeItem<ProjectResource>> {
- let treeItem;
- if (parentItemId) {
- treeItem = findTreeItem(tree, parentItemId);
- if (treeItem) {
- treeItem.status = TreeItemStatus.LOADED;
- }
- }
- const items = projects.map(p => ({
- id: p.uuid,
- open: false,
- active: false,
- status: TreeItemStatus.INITIAL,
- data: p,
- items: []
- } as TreeItem<ProjectResource>));
-
- if (treeItem) {
- treeItem.items = items;
- return tree;
- }
-
- return items;
-}
-
-const initialState: ProjectState = {
- items: [],
- currentItemId: ""
-};
-
-
-export const projectsReducer = (state: ProjectState = initialState, action: ProjectAction) => {
- return projectActions.match(action, {
- REMOVE_PROJECT: () => state,
- PROJECTS_REQUEST: itemId => {
- const items = _.cloneDeep(state.items);
- const item = findTreeItem(items, itemId);
- if (item) {
- item.status = TreeItemStatus.PENDING;
- state.items = items;
- }
- return { ...state, items };
- },
- PROJECTS_SUCCESS: ({ projects, parentItemId }) => {
- const items = _.cloneDeep(state.items);
- return {
- ...state,
- items: updateProjectTree(items, projects, parentItemId)
- };
- },
- TOGGLE_PROJECT_TREE_ITEM_OPEN: itemId => {
- const items = _.cloneDeep(state.items);
- const item = findTreeItem(items, itemId);
- if (item) {
- item.open = !item.open;
- }
- return {
- ...state,
- items,
- currentItemId: itemId
- };
- },
- TOGGLE_PROJECT_TREE_ITEM_ACTIVE: itemId => {
- const items = _.cloneDeep(state.items);
- resetTreeActivity(items);
- const item = findTreeItem(items, itemId);
- if (item) {
- item.active = true;
- }
- return {
- ...state,
- items,
- currentItemId: itemId
- };
- },
- RESET_PROJECT_TREE_ACTIVITY: () => {
- const items = _.cloneDeep(state.items);
- resetTreeActivity(items);
- return {
- ...state,
- items,
- currentItemId: ""
- };
- },
- default: () => state
- });
-};
import { Dispatch } from "redux";
import { initialize, startSubmit, stopSubmit } from 'redux-form';
import { RootState } from "~/store/store";
-import { loadDetailsPanel } from "~/store/details-panel/details-panel-action";
import { dialogActions } from "~/store/dialog/dialog-actions";
-import { snackbarActions } from "~/store/snackbar/snackbar-actions";
import { ContextMenuResource } from '~/store/context-menu/context-menu-reducer';
import { getCommonResourceServiceError, CommonResourceServiceError } from "~/common/api/common-resource-service";
import { ServiceRepository } from "~/services/services";
import { ProjectResource } from '~/models/project';
-import { getProjectList } from '~/store/project/project-action';
-import { projectPanelActions } from '~/store/project-panel/project-panel-action';
export interface ProjectUpdateFormDialogData {
uuid: string;
import thunkMiddleware from 'redux-thunk';
import { History } from "history";
-import { projectsReducer } from "./project/project-reducer";
import { authReducer } from "./auth/auth-reducer";
import { dataExplorerReducer } from './data-explorer/data-explorer-reducer';
import { detailsPanelReducer } from './details-panel/details-panel-reducer';
const createRootReducer = (services: ServiceRepository) => combineReducers({
auth: authReducer(services),
- projects: projectsReducer,
collections: collectionsReducer,
router: routerReducer,
dataExplorer: dataExplorerReducer,
import { DispatchProp, connect } from 'react-redux';
import { DataColumns } from '~/components/data-table/data-table';
import { RouteComponentProps } from 'react-router';
-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 } from '~/models/resource';
import { resourceLabel } from '~/common/labels';
import { ArvadosTheme } from '~/common/custom-theme';
-import { FAVORITE_PANEL_ID, loadFavoritePanel } from "~/store/favorite-panel/favorite-panel-action";
+import { FAVORITE_PANEL_ID } from "~/store/favorite-panel/favorite-panel-action";
import { ResourceFileSize, ResourceLastModifiedDate, ProcessStatus, ResourceType, ResourceOwner, ResourceName } from '~/views-components/data-explorer/renderers';
import { FavoriteIcon } from '~/components/icon/icon';
import { Dispatch } from 'redux';
& WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
export const FavoritePanel = withStyles(styles)(
- connect((state: RootState) => ({ currentItemId: state.projects.currentItemId }), mapDispatchToProps)(
+ connect(undefined, mapDispatchToProps)(
class extends React.Component<FavoritePanelProps> {
render() {
return <DataExplorer
import { RootState } from "~/store/store";
import { MainAppBar, MainAppBarActionProps, MainAppBarMenuItem } from '~/views-components/main-app-bar/main-app-bar';
import { push } from 'react-router-redux';
-import { TreeItem } from "~/components/tree/tree";
import { ProjectPanel } from "~/views/project-panel/project-panel";
import { DetailsPanel } from '~/views-components/details-panel/details-panel';
import { ArvadosTheme } from '~/common/custom-theme';
import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
-import { ProjectResource } from '~/models/project';
import { ContextMenu } from "~/views-components/context-menu/context-menu";
import { FavoritePanel } from "../favorite-panel/favorite-panel";
import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
});
interface WorkbenchDataProps {
- projects: Array<TreeItem<ProjectResource>>;
- currentProjectId: string;
user?: User;
currentToken?: string;
}
export const Workbench = withStyles(styles)(
connect<WorkbenchDataProps>(
(state: RootState) => ({
- projects: state.projects.items,
- currentProjectId: state.projects.currentItemId,
user: state.auth.user,
currentToken: state.auth.apiToken,
})