· 5 years ago · Mar 10, 2021, 08:38 AM
1// React Dependencies
2import React, { useState, useEffect, Fragment } from 'react'
3import { Link, useHistory, useParams } from "react-router-dom";
4import { uniq, set } from "lodash";
5// Material UI Components - Core
6import { TreeView } from '@material-ui/lab';
7// Material UI Components - Icons
8import { ArrowDropDown, ArrowRight } from '@material-ui/icons';
9// Redux Setup
10import { useDispatch, useSelector } from "react-redux";
11import { ICombinedState } from "redux/store";
12import {
13 actionResetUserRole,
14 actionSetUserRole
15} from "redux/UserRole/UserRoleAction";
16import {
17 actionEditFolder,
18 actionAddFolderTree,
19 actionUpdateFolderTree,
20} from "redux/Workspace/WorkspaceAction";
21// API Call
22import {
23 apiGetRoleGetByUserIDandWorkspaceID,
24} from "api/settings";
25import {
26 apiEditFolder,
27 apiGetAllFolderByWorkspaceId,
28 apiGetAllFolderByFolderParent,
29} from "api/folder";
30import {
31 apiGetListByFolderId
32} from "api/list";
33// Interface Call
34import { IWorkspace } from "interfaces/IWorkspace";
35import { IFolderTree } from "interfaces/IFolderTree";
36// Custom Components
37import Dialog from "components/Dialog";
38import Button from "components/Button";
39import Tree from './tree';
40// // Styles
41import useStyles, { StyledTreeItem } from "./styles";
42import { withStyles } from '@material-ui/core/styles';
43import Menu, { MenuProps } from '@material-ui/core/Menu';
44import MenuItem from '@material-ui/core/MenuItem';
45import './antd.css';
46// // Assets
47// // Local Storage
48import { getLocal } from "local/localStorage";
49// // Utils
50import extractFolderPath from "utils/extractFolderPath";
51import { classNames } from "utils/classnames";
52import ListItemIcon from '@material-ui/core/ListItemIcon';
53import ListItemText from '@material-ui/core/ListItemText';
54import InboxIcon from '@material-ui/icons/MoveToInbox';
55import DraftsIcon from '@material-ui/icons/Drafts';
56import SendIcon from '@material-ui/icons/Send'
57
58interface ReduxState {
59 workspaces: IWorkspace[];
60 folderTrees: IFolderTree[];
61}
62
63const TreeNode = Tree.TreeNode;
64const { DirectoryTree } = Tree;
65
66const StyledMenu = withStyles({
67 paper: {
68 border: '1px solid #d3d4d5',
69 },
70})((props: MenuProps) => (
71 <Menu
72 elevation={0}
73 getContentAnchorEl={null}
74 anchorOrigin={{
75 vertical: 'bottom',
76 horizontal: 'center',
77 }}
78 transformOrigin={{
79 vertical: 'top',
80 horizontal: 'center',
81 }}
82 {...props}
83 />
84));
85
86const StyledMenuItem = withStyles((theme) => ({
87 root: {
88 // '&:focus': {
89 // backgroundColor: theme.palette.primary.main,
90 // '& .MuiListItemIcon-root, & .MuiListItemText-primary': {
91 // color: theme.palette.common.white,
92 // },
93 // },
94 },
95}))(MenuItem);
96
97let userRoot = {
98 id: 0,
99 name: "",
100 workspace_id: 0,
101 info: {
102 workspace_configuration: {
103 create: true,
104 edit: true,
105 delete: true,
106 add_member: true,
107 folder_management: true,
108 user_management: true
109 },
110 card_configuration: {
111 create: true,
112 edit: true,
113 delete: true,
114 add_member: true,
115 visibility: 1,
116 add_meeting: true,
117 assign_member: true,
118 change_status: true
119 }
120 },
121 created_at: "",
122 created_by: 0,
123 updated_by: 0
124}
125
126const SidebarTree: React.FC<any> = () => {
127 const classes = useStyles();
128 const history = useHistory();
129 const dispatch = useDispatch();
130 const [gData, setGData] = useState<any>([]);
131 const [expandedKeys, setExpandedKeys] = useState<any>([]);
132 const [openDialogErrorDragDropFolder, setOpenDialogErrorDragDropFolder] = useState<boolean>(false);
133 const [errorMsgDragDropFolder, setErrorMsgDragDropFolder] = useState<string>("");
134 const [anchorEl, setAnchorEl]:any = React.useState<null | HTMLElement>(null);
135 const userId = Number(getLocal("user_id"));
136 let { workspaceId: workspaceIdByUrl, folderId: folderIdByUrl, cardId: cardIdGetByUrl }: any = useParams();
137 const { workspaces, folderTrees } = useSelector<ICombinedState, ReduxState>((state) => {
138 return {
139 workspaces: state.workspace.workspaces,
140 folderTrees: state.workspace.folderTrees,
141 };
142 });
143
144 const handle = {
145 onDragEnter: (info:any) => {
146 console.log('info onDragEnter', info);
147 // expandedKeys 需要受控时设置
148 // this.setState({
149 // expandedKeys: info.expandedKeys,
150 // });
151 },
152 onDrop: (info:any) => {
153 console.log('info onDrop', info);
154 const dropKey = info.node.props.eventKey;
155 const dragKey = info.dragNode.props.eventKey;
156 const dropPos = info.node.props.pos.split('-');
157 const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
158
159 console.log('dropKey', dropKey);
160 console.log('dragKey', dragKey);
161 console.log('dropPos', dropPos);
162 console.log('dropPosition', dropPosition);
163
164 const loop = (data:any, key:any, callback:any) => {
165 data.forEach((item:any, index:any, arr:any) => {
166 if (item.key === key) {
167 return callback(item, index, arr);
168 }
169 if (item.children) {
170 return loop(item.children, key, callback);
171 }
172 });
173 };
174 const data = [...gData];
175
176 // Find dragObject
177 let dragObj:any;
178 loop(data, dragKey, (item:any, index:any, arr:any) => {
179 arr.splice(index, 1);
180 dragObj = item;
181 });
182
183 if (info.dropToGap) {
184 let ar:any;
185 let i:any;
186 loop(data, dropKey, (item:any, index:any, arr:any) => {
187 ar = arr;
188 i = index;
189 });
190 if (dropPosition === -1) {
191 ar.splice(i, 0, dragObj);
192 } else {
193 // drag node and drop node in the same level
194 // and drop to the last node
195 if (dragKey.length === dropKey.length && ar.length - 1 === i) {
196 i += 2;
197 }
198 ar.splice(i - 1, 0, dragObj);
199 }
200 } else {
201 loop(data, dropKey, (item:any) => {
202 item.children = item.children || [];
203 // where to insert 示例添加到尾部,可以是随意位置
204 item.children.push(dragObj);
205 });
206 }
207 console.log('data', data)
208 setGData(data);
209 },
210 onSelect: (selectedKeys: React.Key[], info: any) => {
211 console.log('selected', selectedKeys, info);
212 // if(selectedKeys.length !== 0) {
213 setExpandedKeys(selectedKeys)
214 // }
215 },
216
217 closeDialogErrorDragDropFolder: () => {
218 setErrorMsgDragDropFolder("")
219 setOpenDialogErrorDragDropFolder(false)
220 },
221 setUserRole: async () => {
222 // Check whether the user is included in this workspace.
223 const oneOfMyListWorkspaces = await workspaces.filter((workspace: IWorkspace) => workspace.id === Number(workspaceIdByUrl))
224 const getRoleGetByUserIDandWorkspaceID = await apiGetRoleGetByUserIDandWorkspaceID(Number(workspaceIdByUrl));
225
226 if(getRoleGetByUserIDandWorkspaceID.status === 200) {
227 if(oneOfMyListWorkspaces.length !== 0) {
228 if(oneOfMyListWorkspaces[0].created_by === userId) {
229 dispatch(actionSetUserRole(
230 userRoot
231 ));
232 } else {
233 dispatch(actionSetUserRole(
234 getRoleGetByUserIDandWorkspaceID.data.data
235 ));
236 } } else {
237 dispatch(actionSetUserRole(
238 getRoleGetByUserIDandWorkspaceID.data.data
239 ));
240 }
241 } else {
242 if(oneOfMyListWorkspaces.length !== 0) {
243 if(oneOfMyListWorkspaces[0].created_by === userId) {
244 dispatch(actionSetUserRole(
245 userRoot
246 ));
247 } else {
248 dispatch(actionResetUserRole());
249 }
250 } else {
251 dispatch(actionResetUserRole());
252 }
253 }
254 },
255 }
256
257 const changeFolderView = (folder: any) => {
258 if(folder.parent_id === "" || folder.parent_id === undefined || folder.parent_id === null) {
259 history.push(`/workspace/w/${folder.id}/`);
260 } else {
261 history.push(`/workspace/w/${folder.workspace_id}/f/${folder.id}`);
262 }
263 };
264
265 const updateWorkspaceList = async (workspaceName: string) => {
266 let newDataFolders = [];
267 const dataFolder: any = await apiGetAllFolderByWorkspaceId(Number(workspaceIdByUrl));
268 if (dataFolder.status === 200) {
269 newDataFolders = dataFolder.data.data;
270 }
271 dispatch(actionUpdateFolderTree({
272 id: Number(workspaceIdByUrl),
273 name: workspaceName,
274 folders: newDataFolders
275 }))
276 }
277
278 function handleOnRightClick(info:{event: React.MouseEvent<Element, MouseEvent>, node:any}){
279 console.log('event', info.event)
280 console.log('node', info.node)
281 if(info) {
282 if(info.event) {
283 setAnchorEl(info.event.currentTarget);
284 }
285 }
286 }
287
288 const handleOnCheck = (checkedKeys: React.Key[], info: any) => {
289 console.log('onCheck', checkedKeys, info);
290 }
291
292 function handleOnExpand(expandedKeys:any, expanded:any) {
293 console.log('expandedKeys', expandedKeys)
294 console.log('expanded', expanded)
295 setExpandedKeys(expandedKeys)
296 }
297
298 const handleClose = () => {
299 setAnchorEl(null);
300 };
301
302 const TreeItem = (data: any) => data.map((item:any) => {
303 if (item.children && item.children.length) {
304 return <TreeNode key={item.key} title={item.key}>{TreeItem(item.children)}</TreeNode>;
305 }
306 return <TreeNode key={item.key} title={item.key} />;
307 });
308
309 let keys:any = {name: 'title', id: 'key', folders: 'children'}
310 function rename(value:any): any {
311 if (!value || typeof value !== 'object') return value;
312
313 if (Array.isArray(value)) return value.map(rename);
314
315 return Object.fromEntries(Object
316 .entries(value)
317 .map(([k, v]) => [keys[k] || k, rename(v)])
318 );
319 }
320
321 useEffect(() => {
322 setExpandedKeys([]);
323 // Handle user role in workspace
324 // Every time we change workspace or folder, we will check the user's role in that workspace.
325 // handle.setUserRole();
326 const setDataExpandTree = async () => {
327 let tempFolderPath:any = [`w-${String(workspaceIdByUrl)}`];
328 if(folderIdByUrl !== undefined) {
329 // handle treeview with folder view
330 setExpandedKeys([`w-${String(workspaceIdByUrl)}`])
331 if(folderTrees.length !== 0) {
332 let [...tempFolderTrees]:any = folderTrees;
333 // desctructuring array folderTrees
334 await tempFolderTrees.forEach((folder: any, index:number) => {
335 tempFolderTrees[index] = {
336 folders: tempFolderTrees[index].folders,
337 workspaces_id: tempFolderTrees[index].id, // change properti 'id' to 'workspace_id'
338 name: tempFolderTrees[index].name
339 }
340 })
341
342 let allFolderInCurrentWorkspace: any = tempFolderTrees.filter((wrk: any) => wrk.workspaces_id === Number(workspaceIdByUrl));
343 if(allFolderInCurrentWorkspace.length !== 0) {
344 const getExtractFolderPath = extractFolderPath(allFolderInCurrentWorkspace, Number(folderIdByUrl));
345 getExtractFolderPath.forEach((folder: {
346 title: string,
347 url: {
348 folderId: number | string | null | undefined,
349 workspaceId: number | string | null | undefined,
350 }
351 }) => {
352 tempFolderPath.push(String(folder.url.folderId));
353 })
354 setExpandedKeys(tempFolderPath.reverse())
355 }
356 }
357 } else if(workspaceIdByUrl !== undefined) {
358 setExpandedKeys([`w-${String(workspaceIdByUrl)}`])
359 }
360 }
361 // Handle to set data Tree
362 // Kita hanya mengubah properti yang ada di redux folderTrees untuk disesuaikan dengan library rc-tree agar dapat berjalan dengan baik
363 const setDataGTree = async () => {
364 let result = rename(folderTrees);
365 console.log('result', result);
366 setGData(result);
367 }
368 setDataGTree();
369 // setDataExpandTree();
370 }, []);
371
372
373 if (folderTrees.length === 0) {
374 return (
375 <div className={classes.emptyDataSidebar}>
376 No Workspace Found
377 </div>
378 );
379 } else {
380 return (
381 <Fragment>
382 <div className={classes.canvasRelative}>
383 <div className={classes.treeContainer}>
384 {gData.map((parent: any, index:number) => {
385 return (
386 <DirectoryTree
387 key={index}
388 autoExpandParent={true}
389 className="draggable-tree"
390 defaultExpandedKeys={expandedKeys}
391 // expandedKeys={expandedKeys}
392 draggable
393 blockNode
394 onDragEnter={handle.onDragEnter}
395 onDrop={handle.onDrop}
396 treeData={[parent]}
397 onRightClick={handleOnRightClick}
398 selectable={true}
399 // showLine={true}
400 defaultSelectedKeys={["w-0-0"]}
401 onSelect={handle.onSelect}
402 // onCheck={handleOnCheck}
403 multiple
404 onExpand={handleOnExpand}
405 />
406
407 )
408 })}
409 </div>
410 </div>
411 <Dialog
412 openDialog={openDialogErrorDragDropFolder}
413 title={"Error Drag Drop Folder"}
414 onClose={handle.closeDialogErrorDragDropFolder}
415 actions={
416 <Button
417 color="danger"
418 textColor="light"
419 fontSize="inherit"
420 disableElevation={true}
421 onClick={handle.closeDialogErrorDragDropFolder}
422 >
423 OK
424 </Button>
425 }
426 >
427 <span className={classes.errorMsgDragDropFolder}>{errorMsgDragDropFolder}</span>
428 </Dialog>
429 <StyledMenu
430 id="customized-menu"
431 anchorEl={anchorEl}
432 keepMounted
433 open={Boolean(anchorEl)}
434 onClose={handleClose}
435 >
436 <StyledMenuItem>
437 <ListItemIcon>
438 <SendIcon fontSize="small" />
439 </ListItemIcon>
440 <ListItemText primary="Sent mail" />
441 </StyledMenuItem>
442 <StyledMenuItem>
443 <ListItemIcon>
444 <DraftsIcon fontSize="small" />
445 </ListItemIcon>
446 <ListItemText primary="Drafts" />
447 </StyledMenuItem>
448 <StyledMenuItem>
449 <ListItemIcon>
450 <InboxIcon fontSize="small" />
451 </ListItemIcon>
452 <ListItemText primary="Inbox" />
453 </StyledMenuItem>
454 </StyledMenu>
455
456 </Fragment>
457 );
458 }
459}
460
461export default SidebarTree;