· 5 years ago · Mar 10, 2021, 05: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);
96let userRoot = {
97 id: 0,
98 name: "",
99 workspace_id: 0,
100 info: {
101 workspace_configuration: {
102 create: true,
103 edit: true,
104 delete: true,
105 add_member: true,
106 folder_management: true,
107 user_management: true
108 },
109 card_configuration: {
110 create: true,
111 edit: true,
112 delete: true,
113 add_member: true,
114 visibility: 1,
115 add_meeting: true,
116 assign_member: true,
117 change_status: true
118 }
119 },
120 created_at: "",
121 created_by: 0,
122 updated_by: 0
123}
124
125const SidebarTree: React.FC<any> = () => {
126 const classes = useStyles();
127 const history = useHistory();
128 const dispatch = useDispatch();
129 const [gData, setGData] = useState<any>([]);
130 const [expandedKeys, setExpandedKeys] = useState<any>([]);
131 const [openDialogErrorDragDropFolder, setOpenDialogErrorDragDropFolder] = useState<boolean>(false);
132 const [errorMsgDragDropFolder, setErrorMsgDragDropFolder] = useState<string>("");
133 const [anchorEl, setAnchorEl]:any = React.useState<null | HTMLElement>(null);
134 const userId = Number(getLocal("user_id"));
135 let { workspaceId: workspaceIdByUrl, folderId: folderIdByUrl, cardId: cardIdGetByUrl }: any = useParams();
136 const { workspaces, folderTrees } = useSelector<ICombinedState, ReduxState>((state) => {
137 return {
138 workspaces: state.workspace.workspaces,
139 folderTrees: state.workspace.folderTrees,
140 };
141 });
142
143 const handle = {
144 onDragEnter: (info:any) => {
145 console.log('info onDragEnter', info);
146 // expandedKeys 需要受控时设置
147 // this.setState({
148 // expandedKeys: info.expandedKeys,
149 // });
150 },
151 onDrop: (info:any) => {
152 console.log('info onDrop', info);
153 const dropKey = info.node.props.eventKey;
154 const dragKey = info.dragNode.props.eventKey;
155 const dropPos = info.node.props.pos.split('-');
156 const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
157
158 console.log('dropKey', dropKey);
159 console.log('dragKey', dragKey);
160 console.log('dropPos', dropPos);
161 console.log('dropPosition', dropPosition);
162
163 const loop = (data:any, key:any, callback:any) => {
164 data.forEach((item:any, index:any, arr:any) => {
165 if (item.key === key) {
166 return callback(item, index, arr);
167 }
168 if (item.children) {
169 return loop(item.children, key, callback);
170 }
171 });
172 };
173 const data = [...gData];
174
175 // Find dragObject
176 let dragObj:any;
177 loop(data, dragKey, (item:any, index:any, arr:any) => {
178 arr.splice(index, 1);
179 dragObj = item;
180 });
181
182 if (info.dropToGap) {
183 let ar:any;
184 let i:any;
185 loop(data, dropKey, (item:any, index:any, arr:any) => {
186 ar = arr;
187 i = index;
188 });
189 if (dropPosition === -1) {
190 ar.splice(i, 0, dragObj);
191 } else {
192 // drag node and drop node in the same level
193 // and drop to the last node
194 if (dragKey.length === dropKey.length && ar.length - 1 === i) {
195 i += 2;
196 }
197 ar.splice(i - 1, 0, dragObj);
198 }
199 } else {
200 loop(data, dropKey, (item:any) => {
201 item.children = item.children || [];
202 // where to insert 示例添加到尾部,可以是随意位置
203 item.children.push(dragObj);
204 });
205 }
206 console.log('data', data)
207 setGData(data);
208 },
209 onSelect: (selectedKeys: React.Key[], info: any) => {
210 console.log('selected', selectedKeys, info);
211 // if(selectedKeys.length !== 0) {
212 setExpandedKeys(selectedKeys)
213 // }
214 },
215
216 closeDialogErrorDragDropFolder: () => {
217 setErrorMsgDragDropFolder("")
218 setOpenDialogErrorDragDropFolder(false)
219 },
220 setUserRole: async () => {
221 // Check whether the user is included in this workspace.
222 const oneOfMyListWorkspaces = await workspaces.filter((workspace: IWorkspace) => workspace.id === Number(workspaceIdByUrl))
223 const getRoleGetByUserIDandWorkspaceID = await apiGetRoleGetByUserIDandWorkspaceID(Number(workspaceIdByUrl));
224
225 if(getRoleGetByUserIDandWorkspaceID.status === 200) {
226 if(oneOfMyListWorkspaces.length !== 0) {
227 if(oneOfMyListWorkspaces[0].created_by === userId) {
228 dispatch(actionSetUserRole(
229 userRoot
230 ));
231 } else {
232 dispatch(actionSetUserRole(
233 getRoleGetByUserIDandWorkspaceID.data.data
234 ));
235 } } else {
236 dispatch(actionSetUserRole(
237 getRoleGetByUserIDandWorkspaceID.data.data
238 ));
239 }
240 } else {
241 if(oneOfMyListWorkspaces.length !== 0) {
242 if(oneOfMyListWorkspaces[0].created_by === userId) {
243 dispatch(actionSetUserRole(
244 userRoot
245 ));
246 } else {
247 dispatch(actionResetUserRole());
248 }
249 } else {
250 dispatch(actionResetUserRole());
251 }
252 }
253 },
254 }
255
256 const changeFolderView = (folder: any) => {
257 if(folder.parent_id === "" || folder.parent_id === undefined || folder.parent_id === null) {
258 history.push(`/workspace/w/${folder.id}/`);
259 } else {
260 history.push(`/workspace/w/${folder.workspace_id}/f/${folder.id}`);
261 }
262 };
263
264 const updateWorkspaceList = async (workspaceName: string) => {
265 let newDataFolders = [];
266 const dataFolder: any = await apiGetAllFolderByWorkspaceId(Number(workspaceIdByUrl));
267 if (dataFolder.status === 200) {
268 newDataFolders = dataFolder.data.data;
269 }
270 dispatch(actionUpdateFolderTree({
271 id: Number(workspaceIdByUrl),
272 name: workspaceName,
273 folders: newDataFolders
274 }))
275 }
276
277 function handleOnRightClick(info:{event: React.MouseEvent<Element, MouseEvent>, node:any}){
278 console.log('event', info.event)
279 console.log('node', info.node)
280 if(info) {
281 if(info.event) {
282 setAnchorEl(info.event.currentTarget);
283 }
284 }
285
286 }
287
288 const handleOnCheck = (checkedKeys: React.Key[], info: any) => {
289 console.log('onCheck', checkedKeys, info);
290
291 }
292
293 function handleOnExpand(expandedKeys:any, expanded:any) {
294 console.log('expandedKeys', expandedKeys)
295 console.log('expanded', expanded)
296 setExpandedKeys(expandedKeys)
297 }
298
299 const handleClose = () => {
300 setAnchorEl(null);
301 };
302
303 const TreeItem = (data: any) => data.map((item:any) => {
304 if (item.children && item.children.length) {
305 return <TreeNode key={item.key} title={item.key}>{TreeItem(item.children)}</TreeNode>;
306 }
307 return <TreeNode key={item.key} title={item.key} />;
308 });
309
310 useEffect(() => {
311 setExpandedKeys([]);
312 // Handle user role in workspace
313 // Every time we change workspace or folder, we will check the user's role in that workspace.
314 // handle.setUserRole();
315 const setDataExpandTree = async () => {
316 let tempFolderPath:any = [`w-${String(workspaceIdByUrl)}`];
317 if(folderIdByUrl !== undefined) {
318 // handle treeview with folder view
319 setExpandedKeys([`w-${String(workspaceIdByUrl)}`])
320 if(folderTrees.length !== 0) {
321 let [...tempFolderTrees]:any = folderTrees;
322 // desctructuring array folderTrees
323 await tempFolderTrees.forEach((folder: any, index:number) => {
324 tempFolderTrees[index] = {
325 folders: tempFolderTrees[index].folders,
326 workspaces_id: tempFolderTrees[index].id, // change properti 'id' to 'workspace_id'
327 name: tempFolderTrees[index].name
328 }
329 })
330
331 let allFolderInCurrentWorkspace: any = tempFolderTrees.filter((wrk: any) => wrk.workspaces_id === Number(workspaceIdByUrl));
332 if(allFolderInCurrentWorkspace.length !== 0) {
333 const getExtractFolderPath = extractFolderPath(allFolderInCurrentWorkspace, Number(folderIdByUrl));
334 getExtractFolderPath.forEach((folder: {
335 title: string,
336 url: {
337 folderId: number | string | null | undefined,
338 workspaceId: number | string | null | undefined,
339 }
340 }) => {
341 tempFolderPath.push(String(folder.url.folderId));
342 })
343 setExpandedKeys(tempFolderPath.reverse())
344 }
345 }
346 } else if(workspaceIdByUrl !== undefined) {
347 setExpandedKeys([`w-${String(workspaceIdByUrl)}`])
348 }
349 }
350 // Handle to set data Tree
351 // Kita hanya mengubah properti yang ada di redux folderTrees untuk disesuaikan dengan library rc-tree agar dapat berjalan dengan baik
352 const setDataGTree = async () => {
353 let tempGData:any = [];
354 const deepChange = (data: IFolderTree) => {
355 console.log('data', data)
356 if(!data.hasOwnProperty('folders') || data.folders.length === 0) {
357 return {
358 title: data.name,
359 key: data.id,
360 isLeaf: true,
361
362 info: {
363 ...data
364 }
365 }
366 } else {
367 data.folders.map((dataTemp: IFolderTree) => {
368 return tempGData.push({
369 title: data.name,
370 key: data.id,
371 info: {
372 ...data
373 },
374 children: deepChange(dataTemp)
375 })
376 })
377 }
378 console.log('tempGData', tempGData)
379 }
380 folderTrees.map((folderTree: IFolderTree, index: number) => {
381 const resultChangeDeep = deepChange(folderTree);
382 console.log('resultChangeDeep', resultChangeDeep)
383 });
384 }
385 setDataGTree();
386 // setDataExpandTree();
387
388 setGData([
389 {
390 title: 'w-0-0',
391 key: 'w-0-0',
392 children: [
393 {
394 title: '0-0',
395 key: '0-0',
396 children: [
397 {
398 title: '0-1',
399 key: '0-1',
400 isLeaf: true
401 },
402 {
403 title: '0-2',
404 key: '0-2',
405 isLeaf: true
406 }
407 ]
408 },
409 {
410 title: '0-3',
411 key: '0-3',
412 children: [
413 {
414 title: '0-4',
415 key: '0-4',
416 isLeaf: true
417 },
418 {
419 title: '0-5',
420 key: '0-5',
421 isLeaf: true
422 }
423 ]
424 },
425 ]
426 },
427 {
428 title: 'w-0-1',
429 key: 'w-0-1',
430 children: [
431 {
432 title: '0-6',
433 key: '0-6',
434 children: [
435 {
436 title: '0-1-1',
437 key: '0-1-1',
438 isLeaf: true
439 },
440 {
441 title: '0-2-1',
442 key: '0-2-1',
443 isLeaf: true
444 }
445 ]
446 },
447 {
448 title: '0-3-1',
449 key: '0-3-1',
450 children: [
451 {
452 title: '0-4-1',
453 key: '0-4-1',
454 isLeaf: true
455 },
456 {
457 title: '0-5-1',
458 key: '0-5-1',
459 isLeaf: true
460 }
461 ]
462 },
463 ]
464 },
465 {
466 title: 'w-0-3',
467 key: 'w-0-3',
468 }
469 ]);
470 }, []);
471
472
473 if (folderTrees.length === 0) {
474 return (
475 <div className={classes.emptyDataSidebar}>
476 No Workspace Found
477 </div>
478 );
479 } else {
480 return (
481 <Fragment>
482 <div className={classes.canvasRelative}>
483 <div className={classes.treeContainer}>
484 {/* {folderTrees.map((parent: any, index:number) => { */}
485 {gData.map((parent: any, index:number) => {
486 return (
487 <DirectoryTree
488 key={index}
489 autoExpandParent={true}
490 className="draggable-tree"
491 defaultExpandedKeys={expandedKeys}
492 // expandedKeys={expandedKeys}
493 draggable
494 blockNode
495 onDragEnter={handle.onDragEnter}
496 onDrop={handle.onDrop}
497 treeData={[parent]}
498 onRightClick={handleOnRightClick}
499 selectable={true}
500 // showLine={true}
501 defaultSelectedKeys={["w-0-0"]}
502 onSelect={handle.onSelect}
503 // onCheck={handleOnCheck}
504 multiple
505 onExpand={handleOnExpand}
506 />
507
508 )
509 })}
510 </div>
511 </div>
512 <Dialog
513 openDialog={openDialogErrorDragDropFolder}
514 title={"Error Drag Drop Folder"}
515 onClose={handle.closeDialogErrorDragDropFolder}
516 actions={
517 <Button
518 color="danger"
519 textColor="light"
520 fontSize="inherit"
521 disableElevation={true}
522 onClick={handle.closeDialogErrorDragDropFolder}
523 >
524 OK
525 </Button>
526 }
527 >
528 <span className={classes.errorMsgDragDropFolder}>{errorMsgDragDropFolder}</span>
529 </Dialog>
530 <StyledMenu
531 id="customized-menu"
532 anchorEl={anchorEl}
533 keepMounted
534 open={Boolean(anchorEl)}
535 onClose={handleClose}
536 >
537 <StyledMenuItem>
538 <ListItemIcon>
539 <SendIcon fontSize="small" />
540 </ListItemIcon>
541 <ListItemText primary="Sent mail" />
542 </StyledMenuItem>
543 <StyledMenuItem>
544 <ListItemIcon>
545 <DraftsIcon fontSize="small" />
546 </ListItemIcon>
547 <ListItemText primary="Drafts" />
548 </StyledMenuItem>
549 <StyledMenuItem>
550 <ListItemIcon>
551 <InboxIcon fontSize="small" />
552 </ListItemIcon>
553 <ListItemText primary="Inbox" />
554 </StyledMenuItem>
555 </StyledMenu>
556
557 </Fragment>
558 );
559 }
560}
561
562export default SidebarTree;