chore: move Kanban code to separate module
This commit is contained in:
parent
16eb9e165f
commit
beaa215bc2
@ -16,7 +16,7 @@ const Routes = ({ history }: RoutesProps) => (
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" component={Dashboard} />
|
<Route exact path="/" component={Dashboard} />
|
||||||
<Route exact path="/projects" component={Projects} />
|
<Route exact path="/projects" component={Projects} />
|
||||||
<Route exact path="/projects/:projectId" component={Project} />
|
<Route path="/projects/:projectId" component={Project} />
|
||||||
<Route exact path="/login" component={Login} />
|
<Route exact path="/login" component={Login} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
5
web/src/Projects/Project/KanbanBoard/Styles.ts
Normal file
5
web/src/Projects/Project/KanbanBoard/Styles.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Board = styled.div`
|
||||||
|
margin-left: 36px;
|
||||||
|
`;
|
48
web/src/Projects/Project/KanbanBoard/index.tsx
Normal file
48
web/src/Projects/Project/KanbanBoard/index.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useRouteMatch, useHistory } from 'react-router';
|
||||||
|
|
||||||
|
import Lists from 'shared/components/Lists';
|
||||||
|
import { Board } from './Styles';
|
||||||
|
|
||||||
|
type KanbanBoardProps = {
|
||||||
|
listsData: BoardState;
|
||||||
|
onOpenListActionsPopup: (isOpen: boolean, left: number, top: number, taskGroupID: string) => void;
|
||||||
|
onCardDrop: (task: Task) => void;
|
||||||
|
onListDrop: (taskGroup: TaskGroup) => void;
|
||||||
|
onCardCreate: (taskGroupID: string, name: string) => void;
|
||||||
|
onQuickEditorOpen: (e: ContextMenuEvent) => void;
|
||||||
|
onCreateList: (listName: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const KanbanBoard: React.FC<KanbanBoardProps> = ({
|
||||||
|
listsData,
|
||||||
|
onOpenListActionsPopup,
|
||||||
|
onQuickEditorOpen,
|
||||||
|
onCardCreate,
|
||||||
|
onCardDrop,
|
||||||
|
onListDrop,
|
||||||
|
onCreateList,
|
||||||
|
}) => {
|
||||||
|
const match = useRouteMatch();
|
||||||
|
const history = useHistory();
|
||||||
|
return (
|
||||||
|
<Board>
|
||||||
|
<Lists
|
||||||
|
onCardClick={task => {
|
||||||
|
history.push(`${match.url}/c/${task.taskID}`);
|
||||||
|
}}
|
||||||
|
onExtraMenuOpen={(taskGroupID, pos, size) => {
|
||||||
|
onOpenListActionsPopup(true, pos.left, pos.top + size.height + 5, taskGroupID);
|
||||||
|
}}
|
||||||
|
onQuickEditorOpen={onQuickEditorOpen}
|
||||||
|
onCardCreate={onCardCreate}
|
||||||
|
onCardDrop={onCardDrop}
|
||||||
|
onListDrop={onListDrop}
|
||||||
|
{...listsData}
|
||||||
|
onCreateList={onCreateList}
|
||||||
|
/>
|
||||||
|
</Board>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KanbanBoard;
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import produce from 'immer';
|
import * as BoardStateUtils from 'shared/utils/boardState';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
useFindProjectQuery,
|
useFindProjectQuery,
|
||||||
useUpdateTaskNameMutation,
|
useUpdateTaskNameMutation,
|
||||||
@ -15,7 +15,6 @@ import {
|
|||||||
|
|
||||||
import Navbar from 'App/Navbar';
|
import Navbar from 'App/Navbar';
|
||||||
import TopNavbar from 'App/TopNavbar';
|
import TopNavbar from 'App/TopNavbar';
|
||||||
import Lists from 'shared/components/Lists';
|
|
||||||
import QuickCardEditor from 'shared/components/QuickCardEditor';
|
import QuickCardEditor from 'shared/components/QuickCardEditor';
|
||||||
import PopupMenu from 'shared/components/PopupMenu';
|
import PopupMenu from 'shared/components/PopupMenu';
|
||||||
import ListActions from 'shared/components/ListActions';
|
import ListActions from 'shared/components/ListActions';
|
||||||
@ -23,19 +22,11 @@ import Modal from 'shared/components/Modal';
|
|||||||
import TaskDetails from 'shared/components/TaskDetails';
|
import TaskDetails from 'shared/components/TaskDetails';
|
||||||
import MemberManager from 'shared/components/MemberManager';
|
import MemberManager from 'shared/components/MemberManager';
|
||||||
import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
|
import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
|
||||||
|
import KanbanBoard from 'Projects/Project/KanbanBoard';
|
||||||
|
|
||||||
interface ColumnState {
|
type TaskRouteProps = {
|
||||||
[key: string]: TaskGroup;
|
taskID: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TaskState {
|
|
||||||
[key: string]: Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
columns: ColumnState;
|
|
||||||
tasks: TaskState;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface QuickCardEditorState {
|
interface QuickCardEditorState {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -66,15 +57,11 @@ const Title = styled.span`
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Board = styled.div`
|
|
||||||
margin-left: 36px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface ProjectParams {
|
interface ProjectParams {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: State = { tasks: {}, columns: {} };
|
const initialState: BoardState = { tasks: {}, columns: {} };
|
||||||
const initialPopupState = { left: 0, top: 0, isOpen: false, taskGroupID: '' };
|
const initialPopupState = { left: 0, top: 0, isOpen: false, taskGroupID: '' };
|
||||||
const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top: 0, left: 0 };
|
const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top: 0, left: 0 };
|
||||||
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
||||||
@ -83,6 +70,9 @@ const initialTaskDetailsState = { isOpen: false, taskID: '' };
|
|||||||
|
|
||||||
const Project = () => {
|
const Project = () => {
|
||||||
const { projectId } = useParams<ProjectParams>();
|
const { projectId } = useParams<ProjectParams>();
|
||||||
|
const match = useRouteMatch();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const [listsData, setListsData] = useState(initialState);
|
const [listsData, setListsData] = useState(initialState);
|
||||||
const [popupData, setPopupData] = useState(initialPopupState);
|
const [popupData, setPopupData] = useState(initialPopupState);
|
||||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||||
@ -90,92 +80,52 @@ const Project = () => {
|
|||||||
const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
|
const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
|
||||||
const [updateTaskLocation] = useUpdateTaskLocationMutation();
|
const [updateTaskLocation] = useUpdateTaskLocationMutation();
|
||||||
const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
|
const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
|
||||||
|
|
||||||
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
|
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
|
||||||
onCompleted: deletedTaskGroupData => {
|
onCompleted: deletedTaskGroupData => {
|
||||||
const nextState = produce(listsData, (draftState: State) => {
|
setListsData(
|
||||||
delete draftState.columns[deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID];
|
BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID),
|
||||||
const filteredTasks = Object.keys(listsData.tasks)
|
);
|
||||||
.filter(
|
},
|
||||||
taskID =>
|
|
||||||
listsData.tasks[taskID].taskGroup.taskGroupID !==
|
|
||||||
deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID,
|
|
||||||
)
|
|
||||||
.reduce((obj: TaskState, key: string) => {
|
|
||||||
obj[key] = listsData.tasks[key];
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
draftState.tasks = filteredTasks;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setListsData(nextState);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [createTaskGroup] = useCreateTaskGroupMutation({
|
const [createTaskGroup] = useCreateTaskGroupMutation({
|
||||||
onCompleted: newTaskGroupData => {
|
onCompleted: newTaskGroupData => {
|
||||||
const newListsData = {
|
const newTaskGroup = {
|
||||||
...listsData,
|
...newTaskGroupData.createTaskGroup,
|
||||||
columns: {
|
|
||||||
...listsData.columns,
|
|
||||||
[newTaskGroupData.createTaskGroup.taskGroupID]: {
|
|
||||||
taskGroupID: newTaskGroupData.createTaskGroup.taskGroupID,
|
|
||||||
name: newTaskGroupData.createTaskGroup.name,
|
|
||||||
position: newTaskGroupData.createTaskGroup.position,
|
|
||||||
tasks: [],
|
tasks: [],
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
setListsData(newListsData);
|
setListsData(BoardStateUtils.addTaskGroup(listsData, newTaskGroup));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [createTask] = useCreateTaskMutation({
|
const [createTask] = useCreateTaskMutation({
|
||||||
onCompleted: newTaskData => {
|
onCompleted: newTaskData => {
|
||||||
const newListsData = {
|
const newTask = {
|
||||||
...listsData,
|
...newTaskData.createTask,
|
||||||
tasks: {
|
|
||||||
...listsData.tasks,
|
|
||||||
[newTaskData.createTask.taskID]: {
|
|
||||||
taskGroup: {
|
|
||||||
taskGroupID: newTaskData.createTask.taskGroup.taskGroupID,
|
|
||||||
},
|
|
||||||
taskID: newTaskData.createTask.taskID,
|
|
||||||
name: newTaskData.createTask.name,
|
|
||||||
position: newTaskData.createTask.position,
|
|
||||||
labels: [],
|
labels: [],
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
setListsData(newListsData);
|
setListsData(BoardStateUtils.addTask(listsData, newTask));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [deleteTask] = useDeleteTaskMutation({
|
const [deleteTask] = useDeleteTaskMutation({
|
||||||
onCompleted: deletedTask => {
|
onCompleted: deletedTask => {
|
||||||
const { [deletedTask.deleteTask.taskID]: removedTask, ...remainingTasks } = listsData.tasks;
|
setListsData(BoardStateUtils.deleteTask(listsData, deletedTask.deleteTask.taskID));
|
||||||
const newListsData = {
|
|
||||||
...listsData,
|
|
||||||
tasks: remainingTasks,
|
|
||||||
};
|
|
||||||
setListsData(newListsData);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [updateTaskName] = useUpdateTaskNameMutation({
|
const [updateTaskName] = useUpdateTaskNameMutation({
|
||||||
onCompleted: newTaskData => {
|
onCompleted: newTaskData => {
|
||||||
const newListsData = {
|
setListsData(
|
||||||
...listsData,
|
BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.taskID, newTaskData.updateTaskName.name),
|
||||||
tasks: {
|
);
|
||||||
...listsData.tasks,
|
|
||||||
[newTaskData.updateTaskName.taskID]: {
|
|
||||||
...listsData.tasks[newTaskData.updateTaskName.taskID],
|
|
||||||
name: newTaskData.updateTaskName.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
setListsData(newListsData);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { loading, data } = useFindProjectQuery({
|
const { loading, data } = useFindProjectQuery({
|
||||||
variables: { projectId },
|
variables: { projectId },
|
||||||
onCompleted: newData => {
|
onCompleted: newData => {
|
||||||
const newListsData: State = { tasks: {}, columns: {} };
|
const newListsData: BoardState = { tasks: {}, columns: {} };
|
||||||
newData.findProject.taskGroups.forEach(taskGroup => {
|
newData.findProject.taskGroups.forEach(taskGroup => {
|
||||||
newListsData.columns[taskGroup.taskGroupID] = {
|
newListsData.columns[taskGroup.taskGroupID] = {
|
||||||
taskGroupID: taskGroup.taskGroupID,
|
taskGroupID: taskGroup.taskGroupID,
|
||||||
@ -198,37 +148,7 @@ const Project = () => {
|
|||||||
setListsData(newListsData);
|
setListsData(newListsData);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const onCardDrop = (droppedTask: Task) => {
|
|
||||||
console.log(droppedTask);
|
|
||||||
updateTaskLocation({
|
|
||||||
variables: {
|
|
||||||
taskID: droppedTask.taskID,
|
|
||||||
taskGroupID: droppedTask.taskGroup.taskGroupID,
|
|
||||||
position: droppedTask.position,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const newState = {
|
|
||||||
...listsData,
|
|
||||||
tasks: {
|
|
||||||
...listsData.tasks,
|
|
||||||
[droppedTask.taskID]: droppedTask,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
setListsData(newState);
|
|
||||||
};
|
|
||||||
const onListDrop = (droppedColumn: any) => {
|
|
||||||
updateTaskGroupLocation({
|
|
||||||
variables: { taskGroupID: droppedColumn.taskGroupID, position: droppedColumn.position },
|
|
||||||
});
|
|
||||||
const newState = {
|
|
||||||
...listsData,
|
|
||||||
columns: {
|
|
||||||
...listsData.columns,
|
|
||||||
[droppedColumn.taskGroupID]: droppedColumn,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
setListsData(newState);
|
|
||||||
};
|
|
||||||
const onCardCreate = (taskGroupID: string, name: string) => {
|
const onCardCreate = (taskGroupID: string, name: string) => {
|
||||||
const taskGroupTasks = Object.values(listsData.tasks).filter(
|
const taskGroupTasks = Object.values(listsData.tasks).filter(
|
||||||
(task: Task) => task.taskGroup.taskGroupID === taskGroupID,
|
(task: Task) => task.taskGroup.taskGroupID === taskGroupID,
|
||||||
@ -241,6 +161,7 @@ const Project = () => {
|
|||||||
|
|
||||||
createTask({ variables: { taskGroupID, name, position } });
|
createTask({ variables: { taskGroupID, name, position } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onQuickEditorOpen = (e: ContextMenuEvent) => {
|
const onQuickEditorOpen = (e: ContextMenuEvent) => {
|
||||||
const currentTask = Object.values(listsData.tasks).find(task => task.taskID === e.taskID);
|
const currentTask = Object.values(listsData.tasks).find(task => task.taskID === e.taskID);
|
||||||
setQuickCardEditor({
|
setQuickCardEditor({
|
||||||
@ -250,6 +171,33 @@ const Project = () => {
|
|||||||
task: currentTask,
|
task: currentTask,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
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 } });
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Wrapper>Loading</Wrapper>;
|
return <Wrapper>Loading</Wrapper>;
|
||||||
@ -262,38 +210,35 @@ const Project = () => {
|
|||||||
<TopNavbar />
|
<TopNavbar />
|
||||||
<TitleWrapper>
|
<TitleWrapper>
|
||||||
<Title>{data.findProject.name}</Title>
|
<Title>{data.findProject.name}</Title>
|
||||||
</TitleWrapper>
|
<KanbanBoard
|
||||||
<Board>
|
listsData={listsData}
|
||||||
<Lists
|
|
||||||
onCardClick={task => {
|
|
||||||
setTaskDetails({ isOpen: true, taskID: task.taskID });
|
|
||||||
}}
|
|
||||||
onExtraMenuOpen={(taskGroupID, pos, size) => {
|
|
||||||
setPopupData({
|
|
||||||
isOpen: true,
|
|
||||||
left: pos.left,
|
|
||||||
top: pos.top + size.height + 5,
|
|
||||||
taskGroupID,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onQuickEditorOpen={onQuickEditorOpen}
|
|
||||||
onCardCreate={onCardCreate}
|
|
||||||
{...listsData}
|
|
||||||
onCardDrop={onCardDrop}
|
onCardDrop={onCardDrop}
|
||||||
onListDrop={onListDrop}
|
onListDrop={onListDrop}
|
||||||
onCreateList={listName => {
|
onCardCreate={onCardCreate}
|
||||||
const [lastColumn] = Object.values(listsData.columns)
|
onCreateList={onCreateList}
|
||||||
.sort((a, b) => a.position - b.position)
|
onQuickEditorOpen={onQuickEditorOpen}
|
||||||
.slice(-1);
|
onOpenListActionsPopup={(isOpen, left, top, taskGroupID) => {
|
||||||
let position = 65535;
|
setPopupData({ isOpen, top, left, taskGroupID });
|
||||||
if (lastColumn) {
|
|
||||||
position = lastColumn.position * 2 + 1;
|
|
||||||
}
|
|
||||||
createTaskGroup({ variables: { projectID: projectId, name: listName, position } });
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Board>
|
</TitleWrapper>
|
||||||
</MainContent>
|
</MainContent>
|
||||||
|
{popupData.isOpen && (
|
||||||
|
<PopupMenu
|
||||||
|
title="List Actions"
|
||||||
|
top={popupData.top}
|
||||||
|
onClose={() => setPopupData(initialPopupState)}
|
||||||
|
left={popupData.left}
|
||||||
|
>
|
||||||
|
<ListActions
|
||||||
|
taskGroupID={popupData.taskGroupID}
|
||||||
|
onArchiveTaskGroup={taskGroupID => {
|
||||||
|
deleteTaskGroup({ variables: { taskGroupID } });
|
||||||
|
setPopupData(initialPopupState);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopupMenu>
|
||||||
|
)}
|
||||||
{quickCardEditor.isOpen && (
|
{quickCardEditor.isOpen && (
|
||||||
<QuickCardEditor
|
<QuickCardEditor
|
||||||
isOpen
|
isOpen
|
||||||
@ -311,44 +256,19 @@ const Project = () => {
|
|||||||
left={quickCardEditor.left}
|
left={quickCardEditor.left}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{popupData.isOpen && (
|
<Route
|
||||||
<PopupMenu
|
path={`${match.path}/c/:taskID`}
|
||||||
title="List Actions"
|
render={(routeProps: RouteComponentProps<TaskRouteProps>) => (
|
||||||
top={popupData.top}
|
|
||||||
onClose={() => setPopupData(initialPopupState)}
|
|
||||||
left={popupData.left}
|
|
||||||
>
|
|
||||||
<ListActions
|
|
||||||
taskGroupID={popupData.taskGroupID}
|
|
||||||
onArchiveTaskGroup={taskGroupID => {
|
|
||||||
deleteTaskGroup({ variables: { taskGroupID } });
|
|
||||||
setPopupData(initialPopupState);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PopupMenu>
|
|
||||||
)}
|
|
||||||
{memberPopupData.isOpen && (
|
|
||||||
<PopupMenu
|
|
||||||
title="Members"
|
|
||||||
onClose={() => setMemberPopupData(initialMemberPopupState)}
|
|
||||||
top={memberPopupData.top}
|
|
||||||
left={memberPopupData.left}
|
|
||||||
>
|
|
||||||
<MemberManager
|
|
||||||
availableMembers={[{ displayName: 'Jordan Knott', userID: '21345076-6423-4a00-a6bd-cd9f830e2764' }]}
|
|
||||||
activeMembers={[]}
|
|
||||||
onMemberChange={(member, isActive) => console.log(member, isActive)}
|
|
||||||
/>
|
|
||||||
</PopupMenu>
|
|
||||||
)}
|
|
||||||
{taskDetails.isOpen && (
|
|
||||||
<Modal
|
<Modal
|
||||||
width={1040}
|
width={1040}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setTaskDetails(initialTaskDetailsState);
|
history.push(match.url);
|
||||||
}}
|
}}
|
||||||
renderContent={() => {
|
renderContent={() => {
|
||||||
const task = listsData.tasks[taskDetails.taskID];
|
const task = listsData.tasks[routeProps.match.params.taskID];
|
||||||
|
if (!task) {
|
||||||
|
return <div>loading</div>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<TaskDetails
|
<TaskDetails
|
||||||
task={task}
|
task={task}
|
||||||
@ -362,7 +282,7 @@ const Project = () => {
|
|||||||
setTaskDetails(initialTaskDetailsState);
|
setTaskDetails(initialTaskDetailsState);
|
||||||
deleteTask({ variables: { taskID: deletedTask.taskID } });
|
deleteTask({ variables: { taskID: deletedTask.taskID } });
|
||||||
}}
|
}}
|
||||||
onCloseModal={() => setTaskDetails(initialTaskDetailsState)}
|
onCloseModal={() => history.push(match.url)}
|
||||||
onOpenAddMemberPopup={(task, bounds) => {
|
onOpenAddMemberPopup={(task, bounds) => {
|
||||||
console.log(task, bounds);
|
console.log(task, bounds);
|
||||||
setMemberPopupData({
|
setMemberPopupData({
|
||||||
@ -378,6 +298,7 @@ const Project = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ const Projects = () => {
|
|||||||
<TopNavbar />
|
<TopNavbar />
|
||||||
<ProjectGrid>
|
<ProjectGrid>
|
||||||
{projects.map(project => (
|
{projects.map(project => (
|
||||||
<Link to={`/projects/${project.projectID}/`}>
|
<Link to={`/projects/${project.projectID}`}>
|
||||||
<ProjectGridItem project={project} />
|
<ProjectGridItem project={project} />
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
13
web/src/citadel.d.ts
vendored
13
web/src/citadel.d.ts
vendored
@ -1,3 +1,16 @@
|
|||||||
|
interface ColumnState {
|
||||||
|
[key: string]: TaskGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TaskState {
|
||||||
|
[key: string]: Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BoardState {
|
||||||
|
columns: ColumnState;
|
||||||
|
tasks: TaskState;
|
||||||
|
}
|
||||||
|
|
||||||
interface DraggableElement {
|
interface DraggableElement {
|
||||||
id: string;
|
id: string;
|
||||||
position: number;
|
position: number;
|
||||||
|
50
web/src/shared/utils/boardState.ts
Normal file
50
web/src/shared/utils/boardState.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import produce from 'immer';
|
||||||
|
|
||||||
|
export const addTask = (currentState: BoardState, newTask: Task) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
currentState.tasks[newTask.taskID] = newTask;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteTask = (currentState: BoardState, taskID: string) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
delete draftState.tasks[taskID];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addTaskGroup = (currentState: BoardState, newTaskGroup: TaskGroup) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
draftState.columns[newTaskGroup.taskGroupID] = newTaskGroup;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTaskGroup = (currentState: BoardState, newTaskGroup: TaskGroup) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
draftState.columns[newTaskGroup.taskGroupID] = newTaskGroup;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTask = (currentState: BoardState, newTask: Task) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
draftState.tasks[newTask.taskID] = newTask;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteTaskGroup = (currentState: BoardState, deletedTaskGroupID: string) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
delete draftState.columns[deletedTaskGroupID];
|
||||||
|
const filteredTasks = Object.keys(currentState.tasks)
|
||||||
|
.filter(taskID => currentState.tasks[taskID].taskGroup.taskGroupID !== deletedTaskGroupID)
|
||||||
|
.reduce((obj: TaskState, key: string) => {
|
||||||
|
obj[key] = currentState.tasks[key];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
draftState.tasks = filteredTasks;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTaskName = (currentState: BoardState, taskID: string, newName: string) => {
|
||||||
|
return produce(currentState, (draftState: BoardState) => {
|
||||||
|
draftState.tasks[taskID].name = newName;
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user