add dialog create project, form-fields, remove creator from store
[arvados.git] / src / store / project / project-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as _ from "lodash";
6
7 import { projectActions, ProjectAction } from "./project-action";
8 import { TreeItem, TreeItemStatus } from "~/components/tree/tree";
9 import { ProjectResource } from "~/models/project";
10
11 export type ProjectState = {
12     items: Array<TreeItem<ProjectResource>>,
13     currentItemId: string,
14     updater: ProjectUpdater
15 };
16
17 interface ProjectUpdater {
18     opened: boolean;
19     uuid: string;
20 }
21
22 export function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
23     let item;
24     for (const t of tree) {
25         item = t.id === itemId
26             ? t
27             : findTreeItem(t.items ? t.items : [], itemId);
28         if (item) {
29             break;
30         }
31     }
32     return item;
33 }
34
35 export function getActiveTreeItem<T>(tree: Array<TreeItem<T>>): TreeItem<T> | undefined {
36     let item;
37     for (const t of tree) {
38         item = t.active
39             ? t
40             : getActiveTreeItem(t.items ? t.items : []);
41         if (item) {
42             break;
43         }
44     }
45     return item;
46 }
47
48 export function getTreePath<T>(tree: Array<TreeItem<T>>, itemId: string): Array<TreeItem<T>> {
49     for (const item of tree) {
50         if (item.id === itemId) {
51             return [item];
52         } else {
53             const branch = getTreePath(item.items || [], itemId);
54             if (branch.length > 0) {
55                 return [item, ...branch];
56             }
57         }
58     }
59     return [];
60 }
61
62 function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
63     for (const t of tree) {
64         t.active = false;
65         resetTreeActivity(t.items ? t.items : []);
66     }
67 }
68
69 function updateProjectTree(tree: Array<TreeItem<ProjectResource>>, projects: ProjectResource[], parentItemId?: string): Array<TreeItem<ProjectResource>> {
70     let treeItem;
71     if (parentItemId) {
72         treeItem = findTreeItem(tree, parentItemId);
73         if (treeItem) {
74             treeItem.status = TreeItemStatus.LOADED;
75         }
76     }
77     const items = projects.map(p => ({
78         id: p.uuid,
79         open: false,
80         active: false,
81         status: TreeItemStatus.INITIAL,
82         data: p,
83         items: []
84     } as TreeItem<ProjectResource>));
85
86     if (treeItem) {
87         treeItem.items = items;
88         return tree;
89     }
90
91     return items;
92 }
93
94 const updateProject = (state: ProjectState, updater?: Partial<ProjectUpdater>) => ({
95     ...state,
96     updater: {
97         ...state.updater,
98         ...updater
99     }
100 });
101
102 const initialState: ProjectState = {
103     items: [],
104     currentItemId: "",
105     updater: {
106         opened: false,
107         uuid: ''
108     }
109 };
110
111
112 export const projectsReducer = (state: ProjectState = initialState, action: ProjectAction) => {
113     return projectActions.match(action, {
114         OPEN_PROJECT_UPDATER: ({ uuid }) => updateProject(state, { uuid, opened: true }),
115         CLOSE_PROJECT_UPDATER: () => updateProject(state, { opened: false, uuid: "" }),
116         UPDATE_PROJECT_SUCCESS: () => updateProject(state, { opened: false, uuid: "" }),
117         REMOVE_PROJECT: () => state,
118         PROJECTS_REQUEST: itemId => {
119             const items = _.cloneDeep(state.items);
120             const item = findTreeItem(items, itemId);
121             if (item) {
122                 item.status = TreeItemStatus.PENDING;
123                 state.items = items;
124             }
125             return { ...state, items };
126         },
127         PROJECTS_SUCCESS: ({ projects, parentItemId }) => {
128             const items = _.cloneDeep(state.items);
129             return {
130                 ...state,
131                 items: updateProjectTree(items, projects, parentItemId)
132             };
133         },
134         TOGGLE_PROJECT_TREE_ITEM_OPEN: itemId => {
135             const items = _.cloneDeep(state.items);
136             const item = findTreeItem(items, itemId);
137             if (item) {
138                 item.open = !item.open;
139             }
140             return {
141                 ...state,
142                 items,
143                 currentItemId: itemId
144             };
145         },
146         TOGGLE_PROJECT_TREE_ITEM_ACTIVE: itemId => {
147             const items = _.cloneDeep(state.items);
148             resetTreeActivity(items);
149             const item = findTreeItem(items, itemId);
150             if (item) {
151                 item.active = true;
152             }
153             return {
154                 ...state,
155                 items,
156                 currentItemId: itemId
157             };
158         },
159         RESET_PROJECT_TREE_ACTIVITY: () => {
160             const items = _.cloneDeep(state.items);
161             resetTreeActivity(items);
162             return {
163                 ...state,
164                 items,
165                 currentItemId: ""
166             };
167         },
168         default: () => state
169     });
170 };