taskcafe/web/src/Projects/Project/index.tsx

309 lines
10 KiB
TypeScript
Raw Normal View History

2020-04-10 04:40:22 +02:00
import React, { useState } from 'react';
import * as BoardStateUtils from 'shared/utils/boardState';
2020-04-10 04:40:22 +02:00
import styled from 'styled-components/macro';
import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom';
import {
useFindProjectQuery,
useUpdateTaskNameMutation,
useCreateTaskMutation,
useDeleteTaskMutation,
useUpdateTaskLocationMutation,
useUpdateTaskGroupLocationMutation,
useCreateTaskGroupMutation,
2020-04-11 21:24:45 +02:00
useDeleteTaskGroupMutation,
} from 'shared/generated/graphql';
2020-04-10 04:40:22 +02:00
import Navbar from 'App/Navbar';
import TopNavbar from 'App/TopNavbar';
import QuickCardEditor from 'shared/components/QuickCardEditor';
import PopupMenu from 'shared/components/PopupMenu';
import ListActions from 'shared/components/ListActions';
2020-04-13 00:45:51 +02:00
import Modal from 'shared/components/Modal';
import TaskDetails from 'shared/components/TaskDetails';
import MemberManager from 'shared/components/MemberManager';
import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
import KanbanBoard from 'Projects/Project/KanbanBoard';
2020-04-10 04:40:22 +02:00
type TaskRouteProps = {
taskID: string;
};
2020-04-10 04:40:22 +02:00
interface QuickCardEditorState {
isOpen: boolean;
left: number;
top: number;
task?: Task;
2020-04-10 04:40:22 +02:00
}
const MainContent = styled.div`
2020-04-10 18:31:29 +02:00
padding: 0 0 50px 80px;
2020-04-10 04:40:22 +02:00
height: 100%;
background: #262c49;
`;
const Wrapper = styled.div`
font-size: 16px;
background-color: red;
`;
2020-04-10 18:31:29 +02:00
const TitleWrapper = styled.div`
margin-left: 38px;
margin-bottom: 15px;
`;
2020-04-10 04:40:22 +02:00
const Title = styled.span`
text-align: center;
font-size: 24px;
color: #fff;
`;
interface ProjectParams {
projectId: string;
}
const initialState: BoardState = { tasks: {}, columns: {} };
const initialPopupState = { left: 0, top: 0, isOpen: false, taskGroupID: '' };
2020-04-10 04:40:22 +02:00
const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top: 0, left: 0 };
2020-04-13 00:45:51 +02:00
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
const initialLabelsPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
const initialTaskDetailsState = { isOpen: false, taskID: '' };
2020-04-10 04:40:22 +02:00
const Project = () => {
const { projectId } = useParams<ProjectParams>();
const match = useRouteMatch();
const history = useHistory();
2020-04-10 04:40:22 +02:00
const [listsData, setListsData] = useState(initialState);
const [popupData, setPopupData] = useState(initialPopupState);
2020-04-13 00:45:51 +02:00
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
const [taskDetails, setTaskDetails] = useState(initialTaskDetailsState);
2020-04-10 04:40:22 +02:00
const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
const [updateTaskLocation] = useUpdateTaskLocationMutation();
const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
2020-04-11 21:24:45 +02:00
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
onCompleted: deletedTaskGroupData => {
setListsData(
BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID),
);
2020-04-11 21:24:45 +02:00
},
});
const [createTaskGroup] = useCreateTaskGroupMutation({
onCompleted: newTaskGroupData => {
const newTaskGroup = {
...newTaskGroupData.createTaskGroup,
tasks: [],
};
setListsData(BoardStateUtils.addTaskGroup(listsData, newTaskGroup));
},
});
const [createTask] = useCreateTaskMutation({
2020-04-10 04:40:22 +02:00
onCompleted: newTaskData => {
const newTask = {
...newTaskData.createTask,
labels: [],
2020-04-10 04:40:22 +02:00
};
setListsData(BoardStateUtils.addTask(listsData, newTask));
2020-04-10 04:40:22 +02:00
},
});
const [deleteTask] = useDeleteTaskMutation({
2020-04-10 04:40:22 +02:00
onCompleted: deletedTask => {
setListsData(BoardStateUtils.deleteTask(listsData, deletedTask.deleteTask.taskID));
2020-04-10 04:40:22 +02:00
},
});
const [updateTaskName] = useUpdateTaskNameMutation({
2020-04-10 04:40:22 +02:00
onCompleted: newTaskData => {
setListsData(
BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.taskID, newTaskData.updateTaskName.name),
);
2020-04-10 04:40:22 +02:00
},
});
const { loading, data } = useFindProjectQuery({
2020-04-10 04:40:22 +02:00
variables: { projectId },
onCompleted: newData => {
const newListsData: BoardState = { tasks: {}, columns: {} };
newData.findProject.taskGroups.forEach(taskGroup => {
2020-04-10 04:40:22 +02:00
newListsData.columns[taskGroup.taskGroupID] = {
taskGroupID: taskGroup.taskGroupID,
name: taskGroup.name,
position: taskGroup.position,
tasks: [],
};
taskGroup.tasks.forEach(task => {
2020-04-10 04:40:22 +02:00
newListsData.tasks[task.taskID] = {
taskID: task.taskID,
taskGroup: {
taskGroupID: taskGroup.taskGroupID,
},
2020-04-10 04:40:22 +02:00
name: task.name,
position: task.position,
labels: [],
};
});
});
setListsData(newListsData);
},
});
2020-04-10 04:40:22 +02:00
const onCardCreate = (taskGroupID: string, name: string) => {
const taskGroupTasks = Object.values(listsData.tasks).filter(
(task: Task) => task.taskGroup.taskGroupID === taskGroupID,
);
2020-04-10 22:31:12 +02:00
let position = 65535;
2020-04-10 04:40:22 +02:00
if (taskGroupTasks.length !== 0) {
const [lastTask] = taskGroupTasks.sort((a: any, b: any) => a.position - b.position).slice(-1);
position = Math.ceil(lastTask.position) * 2 + 1;
}
2020-04-10 22:31:12 +02:00
createTask({ variables: { taskGroupID, name, position } });
2020-04-10 04:40:22 +02:00
};
2020-04-10 04:40:22 +02:00
const onQuickEditorOpen = (e: ContextMenuEvent) => {
const currentTask = Object.values(listsData.tasks).find(task => task.taskID === e.taskID);
2020-04-10 04:40:22 +02:00
setQuickCardEditor({
top: e.top,
left: e.left,
isOpen: true,
task: currentTask,
2020-04-10 04:40:22 +02:00
});
};
const onCardDrop = (droppedTask: Task) => {
updateTaskLocation({
variables: {
taskID: droppedTask.taskID,
taskGroupID: droppedTask.taskGroup.taskGroupID,
position: droppedTask.position,
},
});
setListsData(BoardStateUtils.updateTask(listsData, droppedTask));
};
const onListDrop = (droppedColumn: TaskGroup) => {
updateTaskGroupLocation({
variables: { taskGroupID: droppedColumn.taskGroupID, position: droppedColumn.position },
});
setListsData(BoardStateUtils.updateTaskGroup(listsData, droppedColumn));
};
const onCreateList = (listName: string) => {
const [lastColumn] = Object.values(listsData.columns)
.sort((a, b) => a.position - b.position)
.slice(-1);
let position = 65535;
if (lastColumn) {
position = lastColumn.position * 2 + 1;
}
createTaskGroup({ variables: { projectID: projectId, name: listName, position } });
};
2020-04-10 04:40:22 +02:00
if (loading) {
return <Wrapper>Loading</Wrapper>;
}
if (data) {
return (
<>
<Navbar />
<MainContent>
<TopNavbar />
2020-04-10 18:31:29 +02:00
<TitleWrapper>
<Title>{data.findProject.name}</Title>
<KanbanBoard
listsData={listsData}
2020-04-10 18:31:29 +02:00
onCardDrop={onCardDrop}
onListDrop={onListDrop}
onCardCreate={onCardCreate}
onCreateList={onCreateList}
onQuickEditorOpen={onQuickEditorOpen}
onOpenListActionsPopup={(isOpen, left, top, taskGroupID) => {
setPopupData({ isOpen, top, left, taskGroupID });
}}
2020-04-10 18:31:29 +02:00
/>
</TitleWrapper>
2020-04-10 04:40:22 +02:00
</MainContent>
{popupData.isOpen && (
<PopupMenu
title="List Actions"
top={popupData.top}
onClose={() => setPopupData(initialPopupState)}
left={popupData.left}
>
2020-04-11 21:24:45 +02:00
<ListActions
taskGroupID={popupData.taskGroupID}
onArchiveTaskGroup={taskGroupID => {
deleteTaskGroup({ variables: { taskGroupID } });
setPopupData(initialPopupState);
}}
/>
</PopupMenu>
)}
{quickCardEditor.isOpen && (
<QuickCardEditor
isOpen
taskID={quickCardEditor.task ? quickCardEditor.task.taskID : ''}
taskGroupID={quickCardEditor.task ? quickCardEditor.task.taskGroup.taskGroupID : ''}
cardTitle={quickCardEditor.task ? quickCardEditor.task.name : ''}
onCloseEditor={() => setQuickCardEditor(initialQuickCardEditorState)}
onEditCard={(_listId: string, cardId: string, cardName: string) => {
updateTaskName({ variables: { taskID: cardId, name: cardName } });
2020-04-13 00:45:51 +02:00
}}
onOpenPopup={() => console.log()}
onArchiveCard={(_listId: string, cardId: string) => deleteTask({ variables: { taskID: cardId } })}
labels={[]}
top={quickCardEditor.top}
left={quickCardEditor.left}
2020-04-13 00:45:51 +02:00
/>
)}
<Route
path={`${match.path}/c/:taskID`}
render={(routeProps: RouteComponentProps<TaskRouteProps>) => (
<Modal
width={1040}
onClose={() => {
history.push(match.url);
}}
renderContent={() => {
const task = listsData.tasks[routeProps.match.params.taskID];
if (!task) {
return <div>loading</div>;
}
return (
<TaskDetails
task={task}
onTaskNameChange={(updatedTask, newName) => {
updateTaskName({ variables: { taskID: updatedTask.taskID, name: newName } });
}}
onTaskDescriptionChange={(updatedTask, newDescription) => {
console.log(updatedTask, newDescription);
}}
onDeleteTask={deletedTask => {
setTaskDetails(initialTaskDetailsState);
deleteTask({ variables: { taskID: deletedTask.taskID } });
}}
onCloseModal={() => history.push(match.url)}
onOpenAddMemberPopup={(task, bounds) => {
console.log(task, bounds);
setMemberPopupData({
isOpen: true,
taskID: task.taskID,
top: bounds.position.top + bounds.size.height + 10,
left: bounds.position.left,
});
}}
onOpenAddLabelPopup={(task, bounds) => {}}
/>
);
}}
/>
)}
/>
2020-04-10 04:40:22 +02:00
</>
);
}
return <Wrapper>Error</Wrapper>;
};
export default Project;