bugfix: fix user checklist item toggle completion
This commit is contained in:
parent
d8daa60729
commit
1e9813601e
@ -1,26 +1,26 @@
|
|||||||
import React, {useState, useContext, useEffect} from 'react';
|
import React, { useState, useContext, useEffect } from 'react';
|
||||||
import Modal from 'shared/components/Modal';
|
import Modal from 'shared/components/Modal';
|
||||||
import TaskDetails from 'shared/components/TaskDetails';
|
import TaskDetails from 'shared/components/TaskDetails';
|
||||||
import PopupMenu, {Popup, usePopup} from 'shared/components/PopupMenu';
|
import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||||
import MemberManager from 'shared/components/MemberManager';
|
import MemberManager from 'shared/components/MemberManager';
|
||||||
import {useRouteMatch, useHistory} from 'react-router';
|
import { useRouteMatch, useHistory } from 'react-router';
|
||||||
import {
|
import {
|
||||||
useDeleteTaskChecklistMutation,
|
useDeleteTaskChecklistMutation,
|
||||||
useUpdateTaskChecklistNameMutation,
|
useUpdateTaskChecklistNameMutation,
|
||||||
useUpdateTaskChecklistItemLocationMutation,
|
useUpdateTaskChecklistItemLocationMutation,
|
||||||
useCreateTaskChecklistMutation,
|
useCreateTaskChecklistMutation,
|
||||||
useFindTaskQuery,
|
useFindTaskQuery,
|
||||||
useUpdateTaskDueDateMutation,
|
useUpdateTaskDueDateMutation,
|
||||||
useSetTaskCompleteMutation,
|
useSetTaskCompleteMutation,
|
||||||
useAssignTaskMutation,
|
useAssignTaskMutation,
|
||||||
useUnassignTaskMutation,
|
useUnassignTaskMutation,
|
||||||
useSetTaskChecklistItemCompleteMutation,
|
useSetTaskChecklistItemCompleteMutation,
|
||||||
useUpdateTaskChecklistLocationMutation,
|
useUpdateTaskChecklistLocationMutation,
|
||||||
useDeleteTaskChecklistItemMutation,
|
useDeleteTaskChecklistItemMutation,
|
||||||
useUpdateTaskChecklistItemNameMutation,
|
useUpdateTaskChecklistItemNameMutation,
|
||||||
useCreateTaskChecklistItemMutation,
|
useCreateTaskChecklistItemMutation,
|
||||||
FindTaskDocument,
|
FindTaskDocument,
|
||||||
FindTaskQuery,
|
FindTaskQuery,
|
||||||
} from 'shared/generated/graphql';
|
} from 'shared/generated/graphql';
|
||||||
import UserIDContext from 'App/context';
|
import UserIDContext from 'App/context';
|
||||||
import MiniProfile from 'shared/components/MiniProfile';
|
import MiniProfile from 'shared/components/MiniProfile';
|
||||||
@ -29,27 +29,27 @@ import produce from 'immer';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Button from 'shared/components/Button';
|
import Button from 'shared/components/Button';
|
||||||
import Input from 'shared/components/Input';
|
import Input from 'shared/components/Input';
|
||||||
import {useForm} from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import updateApolloCache from 'shared/utils/cache';
|
import updateApolloCache from 'shared/utils/cache';
|
||||||
|
|
||||||
const calculateChecklistBadge = (checklists: Array<TaskChecklist>) => {
|
const calculateChecklistBadge = (checklists: Array<TaskChecklist>) => {
|
||||||
const total = checklists.reduce((prev: any, next: any) => {
|
const total = checklists.reduce((prev: any, next: any) => {
|
||||||
return (
|
return (
|
||||||
prev +
|
prev +
|
||||||
next.items.reduce((innerPrev: any, _item: any) => {
|
next.items.reduce((innerPrev: any, _item: any) => {
|
||||||
return innerPrev + 1;
|
return innerPrev + 1;
|
||||||
}, 0)
|
}, 0)
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
const complete = checklists.reduce(
|
||||||
|
(prev: any, next: any) =>
|
||||||
|
prev +
|
||||||
|
next.items.reduce((innerPrev: any, item: any) => {
|
||||||
|
return innerPrev + (item.complete ? 1 : 0);
|
||||||
|
}, 0),
|
||||||
|
0,
|
||||||
);
|
);
|
||||||
}, 0);
|
return { total, complete };
|
||||||
const complete = checklists.reduce(
|
|
||||||
(prev: any, next: any) =>
|
|
||||||
prev +
|
|
||||||
next.items.reduce((innerPrev: any, item: any) => {
|
|
||||||
return innerPrev + (item.complete ? 1 : 0);
|
|
||||||
}, 0),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
return {total, complete};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeleteChecklistButton = styled(Button)`
|
const DeleteChecklistButton = styled(Button)`
|
||||||
@ -58,7 +58,7 @@ const DeleteChecklistButton = styled(Button)`
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
`;
|
`;
|
||||||
type CreateChecklistData = {
|
type CreateChecklistData = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
const CreateChecklistForm = styled.form`
|
const CreateChecklistForm = styled.form`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -80,423 +80,437 @@ const InputError = styled.span`
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
`;
|
`;
|
||||||
type CreateChecklistPopupProps = {
|
type CreateChecklistPopupProps = {
|
||||||
onCreateChecklist: (data: CreateChecklistData) => void;
|
onCreateChecklist: (data: CreateChecklistData) => void;
|
||||||
};
|
};
|
||||||
const CreateChecklistPopup: React.FC<CreateChecklistPopupProps> = ({onCreateChecklist}) => {
|
const CreateChecklistPopup: React.FC<CreateChecklistPopupProps> = ({ onCreateChecklist }) => {
|
||||||
const {register, handleSubmit, errors} = useForm<CreateChecklistData>();
|
const { register, handleSubmit, errors } = useForm<CreateChecklistData>();
|
||||||
const createUser = (data: CreateChecklistData) => {
|
const createUser = (data: CreateChecklistData) => {
|
||||||
onCreateChecklist(data);
|
onCreateChecklist(data);
|
||||||
};
|
};
|
||||||
console.log(errors);
|
console.log(errors);
|
||||||
return (
|
return (
|
||||||
<CreateChecklistForm onSubmit={handleSubmit(createUser)}>
|
<CreateChecklistForm onSubmit={handleSubmit(createUser)}>
|
||||||
<CreateChecklistInput
|
<CreateChecklistInput
|
||||||
floatingLabel
|
floatingLabel
|
||||||
value="Checklist"
|
value="Checklist"
|
||||||
width="100%"
|
width="100%"
|
||||||
label="Name"
|
label="Name"
|
||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
variant="alternate"
|
variant="alternate"
|
||||||
ref={register({required: 'Checklist name is required'})}
|
ref={register({ required: 'Checklist name is required' })}
|
||||||
/>
|
/>
|
||||||
<CreateChecklistButton type="submit">Create</CreateChecklistButton>
|
<CreateChecklistButton type="submit">Create</CreateChecklistButton>
|
||||||
</CreateChecklistForm>
|
</CreateChecklistForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type DetailsProps = {
|
type DetailsProps = {
|
||||||
taskID: string;
|
taskID: string;
|
||||||
projectURL: string;
|
projectURL: string;
|
||||||
onTaskNameChange: (task: Task, newName: string) => void;
|
onTaskNameChange: (task: Task, newName: string) => void;
|
||||||
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
||||||
onDeleteTask: (task: Task) => void;
|
onDeleteTask: (task: Task) => void;
|
||||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||||
availableMembers: Array<TaskUser>;
|
availableMembers: Array<TaskUser>;
|
||||||
refreshCache: () => void;
|
refreshCache: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialMemberPopupState = {taskID: '', isOpen: false, top: 0, left: 0};
|
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
||||||
|
|
||||||
const Details: React.FC<DetailsProps> = ({
|
const Details: React.FC<DetailsProps> = ({
|
||||||
projectURL,
|
projectURL,
|
||||||
taskID,
|
taskID,
|
||||||
onTaskNameChange,
|
onTaskNameChange,
|
||||||
onTaskDescriptionChange,
|
onTaskDescriptionChange,
|
||||||
onDeleteTask,
|
onDeleteTask,
|
||||||
onOpenAddLabelPopup,
|
onOpenAddLabelPopup,
|
||||||
availableMembers,
|
availableMembers,
|
||||||
refreshCache,
|
refreshCache,
|
||||||
}) => {
|
}) => {
|
||||||
const {userID} = useContext(UserIDContext);
|
const { userID } = useContext(UserIDContext);
|
||||||
const {showPopup, hidePopup} = usePopup();
|
const { showPopup, hidePopup } = usePopup();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
||||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||||
const [updateTaskChecklistLocation] = useUpdateTaskChecklistLocationMutation();
|
const [updateTaskChecklistLocation] = useUpdateTaskChecklistLocationMutation();
|
||||||
const [updateTaskChecklistItemLocation] = useUpdateTaskChecklistItemLocationMutation({
|
const [updateTaskChecklistItemLocation] = useUpdateTaskChecklistItemLocationMutation({
|
||||||
update: (client, response) => {
|
update: (client, response) => {
|
||||||
updateApolloCache<FindTaskQuery>(
|
updateApolloCache<FindTaskQuery>(
|
||||||
client,
|
client,
|
||||||
FindTaskDocument,
|
FindTaskDocument,
|
||||||
cache =>
|
cache =>
|
||||||
produce(cache, draftCache => {
|
produce(cache, draftCache => {
|
||||||
const {prevChecklistID, checklistID, checklistItem} = response.data.updateTaskChecklistItemLocation;
|
const { prevChecklistID, checklistID, checklistItem } = response.data.updateTaskChecklistItemLocation;
|
||||||
console.log(`${checklistID} !== ${prevChecklistID}`);
|
console.log(`${checklistID} !== ${prevChecklistID}`);
|
||||||
if (checklistID !== prevChecklistID) {
|
if (checklistID !== prevChecklistID) {
|
||||||
const oldIdx = cache.findTask.checklists.findIndex(c => c.id === prevChecklistID);
|
const oldIdx = cache.findTask.checklists.findIndex(c => c.id === prevChecklistID);
|
||||||
const newIdx = cache.findTask.checklists.findIndex(c => c.id === checklistID);
|
const newIdx = cache.findTask.checklists.findIndex(c => c.id === checklistID);
|
||||||
console.log(`oldIdx ${oldIdx} newIdx ${newIdx}`);
|
console.log(`oldIdx ${oldIdx} newIdx ${newIdx}`);
|
||||||
if (oldIdx > -1 && newIdx > -1) {
|
if (oldIdx > -1 && newIdx > -1) {
|
||||||
const item = cache.findTask.checklists[oldIdx].items.find(item => item.id === checklistItem.id);
|
const item = cache.findTask.checklists[oldIdx].items.find(item => item.id === checklistItem.id);
|
||||||
console.log(item);
|
console.log(item);
|
||||||
if (item) {
|
if (item) {
|
||||||
draftCache.findTask.checklists[oldIdx].items = cache.findTask.checklists[oldIdx].items.filter(
|
draftCache.findTask.checklists[oldIdx].items = cache.findTask.checklists[oldIdx].items.filter(
|
||||||
i => i.id !== checklistItem.id,
|
i => i.id !== checklistItem.id,
|
||||||
);
|
);
|
||||||
draftCache.findTask.checklists[newIdx].items.push({
|
draftCache.findTask.checklists[newIdx].items.push({
|
||||||
...item,
|
...item,
|
||||||
position: checklistItem.position,
|
position: checklistItem.position,
|
||||||
taskChecklistID: checklistID,
|
taskChecklistID: checklistID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation({
|
|
||||||
update: client => {
|
|
||||||
updateApolloCache<FindTaskQuery>(
|
|
||||||
client,
|
|
||||||
FindTaskDocument,
|
|
||||||
cache =>
|
|
||||||
produce(cache, draftCache => {
|
|
||||||
const {complete, total} = calculateChecklistBadge(draftCache.findTask.checklists);
|
|
||||||
draftCache.findTask.badges.checklist = {
|
|
||||||
__typename: 'ChecklistBadge',
|
|
||||||
complete,
|
|
||||||
total,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [deleteTaskChecklist] = useDeleteTaskChecklistMutation({
|
|
||||||
update: (client, deleteData) => {
|
|
||||||
updateApolloCache<FindTaskQuery>(
|
|
||||||
client,
|
|
||||||
FindTaskDocument,
|
|
||||||
cache =>
|
|
||||||
produce(cache, draftCache => {
|
|
||||||
const {checklists} = cache.findTask;
|
|
||||||
console.log(deleteData)
|
|
||||||
draftCache.findTask.checklists = checklists.filter(c => c.id !== deleteData.data.deleteTaskChecklist.taskChecklist.id);
|
|
||||||
const {complete, total} = calculateChecklistBadge(draftCache.findTask.checklists);
|
|
||||||
draftCache.findTask.badges.checklist = {
|
|
||||||
__typename: 'ChecklistBadge',
|
|
||||||
complete,
|
|
||||||
total,
|
|
||||||
};
|
|
||||||
if (complete === 0 && total === 0) {
|
|
||||||
draftCache.findTask.badges.checklist = null;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [updateTaskChecklistItemName] = useUpdateTaskChecklistItemNameMutation();
|
|
||||||
const [createTaskChecklist] = useCreateTaskChecklistMutation({
|
|
||||||
update: (client, createData) => {
|
|
||||||
updateApolloCache<FindTaskQuery>(
|
|
||||||
client,
|
|
||||||
FindTaskDocument,
|
|
||||||
cache =>
|
|
||||||
produce(cache, draftCache => {
|
|
||||||
const item = createData.data.createTaskChecklist;
|
|
||||||
draftCache.findTask.checklists.push({...item});
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [updateTaskChecklistName] = useUpdateTaskChecklistNameMutation();
|
|
||||||
const [deleteTaskChecklistItem] = useDeleteTaskChecklistItemMutation({
|
|
||||||
update: (client, deleteData) => {
|
|
||||||
updateApolloCache<FindTaskQuery>(
|
|
||||||
client,
|
|
||||||
FindTaskDocument,
|
|
||||||
cache =>
|
|
||||||
produce(cache, draftCache => {
|
|
||||||
const item = deleteData.data.deleteTaskChecklistItem.taskChecklistItem;
|
|
||||||
const targetIdx = cache.findTask.checklists.findIndex(c => c.id === item.taskChecklistID)
|
|
||||||
if (targetIdx > -1) {
|
|
||||||
draftCache.findTask.checklists[targetIdx].items = cache.findTask.checklists[targetIdx].items.filter(c => item.id !== c.id);
|
|
||||||
}
|
|
||||||
const {complete, total} = calculateChecklistBadge(draftCache.findTask.checklists);
|
|
||||||
draftCache.findTask.badges.checklist = {
|
|
||||||
__typename: 'ChecklistBadge',
|
|
||||||
complete,
|
|
||||||
total,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [createTaskChecklistItem] = useCreateTaskChecklistItemMutation({
|
|
||||||
update: (client, newTaskItem) => {
|
|
||||||
updateApolloCache<FindTaskQuery>(
|
|
||||||
client,
|
|
||||||
FindTaskDocument,
|
|
||||||
cache =>
|
|
||||||
produce(cache, draftCache => {
|
|
||||||
const item = newTaskItem.data.createTaskChecklistItem;
|
|
||||||
const {checklists} = cache.findTask;
|
|
||||||
const idx = checklists.findIndex(c => c.id === item.taskChecklistID);
|
|
||||||
if (idx !== -1) {
|
|
||||||
draftCache.findTask.checklists[idx].items.push({...item});
|
|
||||||
const {complete, total} = calculateChecklistBadge(draftCache.findTask.checklists);
|
|
||||||
draftCache.findTask.badges.checklist = {
|
|
||||||
__typename: 'ChecklistBadge',
|
|
||||||
complete,
|
|
||||||
total,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{taskID},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const {loading, data, refetch} = useFindTaskQuery({variables: {taskID}});
|
|
||||||
const [setTaskComplete] = useSetTaskCompleteMutation();
|
|
||||||
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
|
||||||
onCompleted: () => {
|
|
||||||
refetch();
|
|
||||||
refreshCache();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [assignTask] = useAssignTaskMutation({
|
|
||||||
onCompleted: () => {
|
|
||||||
refetch();
|
|
||||||
refreshCache();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [unassignTask] = useUnassignTaskMutation({
|
|
||||||
onCompleted: () => {
|
|
||||||
refetch();
|
|
||||||
refreshCache();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (loading) {
|
|
||||||
return <div>loading</div>;
|
|
||||||
}
|
|
||||||
if (!data) {
|
|
||||||
return <div>loading</div>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Modal
|
|
||||||
width={768}
|
|
||||||
onClose={() => {
|
|
||||||
history.push(projectURL);
|
|
||||||
}}
|
|
||||||
renderContent={() => {
|
|
||||||
return (
|
|
||||||
<TaskDetails
|
|
||||||
task={data.findTask}
|
|
||||||
onChecklistDrop={checklist => {
|
|
||||||
updateTaskChecklistLocation({
|
|
||||||
variables: {checklistID: checklist.id, position: checklist.position},
|
|
||||||
|
|
||||||
optimisticResponse: {
|
|
||||||
__typename: 'Mutation',
|
|
||||||
updateTaskChecklistLocation: {
|
|
||||||
__typename: 'UpdateTaskChecklistLocationPayload',
|
|
||||||
checklist: {
|
|
||||||
__typename: 'TaskChecklist',
|
|
||||||
position: checklist.position,
|
|
||||||
id: checklist.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onChecklistItemDrop={(prevChecklistID, checklistID, checklistItem) => {
|
|
||||||
updateTaskChecklistItemLocation({
|
|
||||||
variables: {checklistID, checklistItemID: checklistItem.id, position: checklistItem.position},
|
|
||||||
|
|
||||||
optimisticResponse: {
|
|
||||||
__typename: 'Mutation',
|
|
||||||
updateTaskChecklistItemLocation: {
|
|
||||||
__typename: 'UpdateTaskChecklistItemLocationPayload',
|
|
||||||
prevChecklistID,
|
|
||||||
checklistID,
|
|
||||||
checklistItem: {
|
|
||||||
__typename: 'TaskChecklistItem',
|
|
||||||
position: checklistItem.position,
|
|
||||||
id: checklistItem.id,
|
|
||||||
taskChecklistID: checklistID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onTaskNameChange={onTaskNameChange}
|
|
||||||
onTaskDescriptionChange={onTaskDescriptionChange}
|
|
||||||
onToggleTaskComplete={task => {
|
|
||||||
setTaskComplete({variables: {taskID: task.id, complete: !task.complete}});
|
|
||||||
}}
|
|
||||||
onDeleteTask={onDeleteTask}
|
|
||||||
onChangeItemName={(itemID, itemName) => {
|
|
||||||
updateTaskChecklistItemName({variables: {taskChecklistItemID: itemID, name: itemName}});
|
|
||||||
}}
|
|
||||||
onCloseModal={() => history.push(projectURL)}
|
|
||||||
onChangeChecklistName={(checklistID, newName) => {
|
|
||||||
updateTaskChecklistName({variables: {taskChecklistID: checklistID, name: newName}});
|
|
||||||
}}
|
|
||||||
onDeleteItem={itemID => {
|
|
||||||
deleteTaskChecklistItem({variables: {taskChecklistItemID: itemID}});
|
|
||||||
}}
|
|
||||||
onToggleChecklistItem={(itemID, complete) => {
|
|
||||||
setTaskChecklistItemComplete({
|
|
||||||
variables: {taskChecklistItemID: itemID, complete},
|
|
||||||
optimisticResponse: {
|
|
||||||
__typename: 'Mutation',
|
|
||||||
setTaskChecklistItemComplete: {
|
|
||||||
__typename: 'TaskChecklistItem',
|
|
||||||
id: itemID,
|
|
||||||
complete,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onAddItem={(taskChecklistID, name, position) => {
|
|
||||||
createTaskChecklistItem({variables: {taskChecklistID, name, position}});
|
|
||||||
}}
|
|
||||||
onMemberProfile={($targetRef, memberID) => {
|
|
||||||
const member = data.findTask.assigned.find(m => m.id === memberID);
|
|
||||||
if (member) {
|
|
||||||
showPopup(
|
|
||||||
$targetRef,
|
|
||||||
<Popup title={null} onClose={() => {}} tab={0}>
|
|
||||||
<MiniProfile
|
|
||||||
user={member}
|
|
||||||
bio="None"
|
|
||||||
onRemoveFromTask={() => {
|
|
||||||
unassignTask({variables: {taskID: data.findTask.id, userID: userID ?? ''}});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Popup>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onOpenAddMemberPopup={(task, $targetRef) => {
|
|
||||||
showPopup(
|
|
||||||
$targetRef,
|
|
||||||
<Popup title="Members" tab={0} onClose={() => {}}>
|
|
||||||
<MemberManager
|
|
||||||
availableMembers={availableMembers}
|
|
||||||
activeMembers={data.findTask.assigned}
|
|
||||||
onMemberChange={(member, isActive) => {
|
|
||||||
if (isActive) {
|
|
||||||
assignTask({variables: {taskID: data.findTask.id, userID: userID ?? ''}});
|
|
||||||
} else {
|
|
||||||
unassignTask({variables: {taskID: data.findTask.id, userID: userID ?? ''}});
|
|
||||||
}
|
}
|
||||||
}}
|
}),
|
||||||
/>
|
{ taskID },
|
||||||
</Popup>,
|
);
|
||||||
);
|
},
|
||||||
}}
|
});
|
||||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation({
|
||||||
onOpenAddChecklistPopup={(_task, $target) => {
|
update: client => {
|
||||||
showPopup(
|
updateApolloCache<FindTaskQuery>(
|
||||||
$target,
|
client,
|
||||||
<Popup
|
FindTaskDocument,
|
||||||
title={'Add checklist'}
|
cache =>
|
||||||
tab={0}
|
produce(cache, draftCache => {
|
||||||
onClose={() => {
|
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
|
||||||
hidePopup();
|
draftCache.findTask.badges.checklist = {
|
||||||
}}
|
__typename: 'ChecklistBadge',
|
||||||
>
|
complete,
|
||||||
<CreateChecklistPopup
|
total,
|
||||||
onCreateChecklist={checklistData => {
|
};
|
||||||
let position = 65535;
|
}),
|
||||||
console.log(data.findTask.checklists);
|
{ taskID },
|
||||||
if (data.findTask.checklists) {
|
);
|
||||||
const [lastChecklist] = data.findTask.checklists.slice(-1);
|
},
|
||||||
console.log(`lastCheclist ${lastChecklist}`);
|
});
|
||||||
if (lastChecklist) {
|
const [deleteTaskChecklist] = useDeleteTaskChecklistMutation({
|
||||||
position = lastChecklist.position * 2 + 1;
|
update: (client, deleteData) => {
|
||||||
}
|
updateApolloCache<FindTaskQuery>(
|
||||||
|
client,
|
||||||
|
FindTaskDocument,
|
||||||
|
cache =>
|
||||||
|
produce(cache, draftCache => {
|
||||||
|
const { checklists } = cache.findTask;
|
||||||
|
console.log(deleteData)
|
||||||
|
draftCache.findTask.checklists = checklists.filter(c => c.id !== deleteData.data.deleteTaskChecklist.taskChecklist.id);
|
||||||
|
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
|
||||||
|
draftCache.findTask.badges.checklist = {
|
||||||
|
__typename: 'ChecklistBadge',
|
||||||
|
complete,
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
if (complete === 0 && total === 0) {
|
||||||
|
draftCache.findTask.badges.checklist = null;
|
||||||
}
|
}
|
||||||
createTaskChecklist({
|
}),
|
||||||
variables: {
|
{ taskID },
|
||||||
taskID: data.findTask.id,
|
);
|
||||||
name: checklistData.name,
|
},
|
||||||
position,
|
});
|
||||||
},
|
const [updateTaskChecklistItemName] = useUpdateTaskChecklistItemNameMutation();
|
||||||
});
|
const [createTaskChecklist] = useCreateTaskChecklistMutation({
|
||||||
hidePopup();
|
update: (client, createData) => {
|
||||||
}}
|
updateApolloCache<FindTaskQuery>(
|
||||||
/>
|
client,
|
||||||
</Popup>,
|
FindTaskDocument,
|
||||||
);
|
cache =>
|
||||||
}}
|
produce(cache, draftCache => {
|
||||||
onDeleteChecklist={($target, checklistID) => {
|
const item = createData.data.createTaskChecklist;
|
||||||
showPopup(
|
draftCache.findTask.checklists.push({ ...item });
|
||||||
$target,
|
}),
|
||||||
<Popup tab={0} title="Delete checklist?" onClose={() => hidePopup()}>
|
{ taskID },
|
||||||
<p>Deleting a checklist is permanent and there is no way to get it back.</p>
|
);
|
||||||
<DeleteChecklistButton
|
},
|
||||||
color="danger"
|
});
|
||||||
onClick={() => {
|
const [updateTaskChecklistName] = useUpdateTaskChecklistNameMutation();
|
||||||
deleteTaskChecklist({variables: {taskChecklistID: checklistID}});
|
const [deleteTaskChecklistItem] = useDeleteTaskChecklistItemMutation({
|
||||||
hidePopup();
|
update: (client, deleteData) => {
|
||||||
}}
|
updateApolloCache<FindTaskQuery>(
|
||||||
>
|
client,
|
||||||
Delete Checklist
|
FindTaskDocument,
|
||||||
|
cache =>
|
||||||
|
produce(cache, draftCache => {
|
||||||
|
const item = deleteData.data.deleteTaskChecklistItem.taskChecklistItem;
|
||||||
|
const targetIdx = cache.findTask.checklists.findIndex(c => c.id === item.taskChecklistID)
|
||||||
|
if (targetIdx > -1) {
|
||||||
|
draftCache.findTask.checklists[targetIdx].items = cache.findTask.checklists[targetIdx].items.filter(c => item.id !== c.id);
|
||||||
|
}
|
||||||
|
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
|
||||||
|
draftCache.findTask.badges.checklist = {
|
||||||
|
__typename: 'ChecklistBadge',
|
||||||
|
complete,
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
{ taskID },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [createTaskChecklistItem] = useCreateTaskChecklistItemMutation({
|
||||||
|
update: (client, newTaskItem) => {
|
||||||
|
updateApolloCache<FindTaskQuery>(
|
||||||
|
client,
|
||||||
|
FindTaskDocument,
|
||||||
|
cache =>
|
||||||
|
produce(cache, draftCache => {
|
||||||
|
const item = newTaskItem.data.createTaskChecklistItem;
|
||||||
|
const { checklists } = cache.findTask;
|
||||||
|
const idx = checklists.findIndex(c => c.id === item.taskChecklistID);
|
||||||
|
if (idx !== -1) {
|
||||||
|
draftCache.findTask.checklists[idx].items.push({ ...item });
|
||||||
|
const { complete, total } = calculateChecklistBadge(draftCache.findTask.checklists);
|
||||||
|
draftCache.findTask.badges.checklist = {
|
||||||
|
__typename: 'ChecklistBadge',
|
||||||
|
complete,
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ taskID },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||||
|
const [setTaskComplete] = useSetTaskCompleteMutation();
|
||||||
|
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [assignTask] = useAssignTaskMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [unassignTask] = useUnassignTaskMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (loading) {
|
||||||
|
return <div>loading</div>;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return <div>loading</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
width={768}
|
||||||
|
onClose={() => {
|
||||||
|
history.push(projectURL);
|
||||||
|
}}
|
||||||
|
renderContent={() => {
|
||||||
|
return (
|
||||||
|
<TaskDetails
|
||||||
|
task={data.findTask}
|
||||||
|
onChecklistDrop={checklist => {
|
||||||
|
updateTaskChecklistLocation({
|
||||||
|
variables: { checklistID: checklist.id, position: checklist.position },
|
||||||
|
|
||||||
|
optimisticResponse: {
|
||||||
|
__typename: 'Mutation',
|
||||||
|
updateTaskChecklistLocation: {
|
||||||
|
__typename: 'UpdateTaskChecklistLocationPayload',
|
||||||
|
checklist: {
|
||||||
|
__typename: 'TaskChecklist',
|
||||||
|
position: checklist.position,
|
||||||
|
id: checklist.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onChecklistItemDrop={(prevChecklistID, checklistID, checklistItem) => {
|
||||||
|
updateTaskChecklistItemLocation({
|
||||||
|
variables: { checklistID, checklistItemID: checklistItem.id, position: checklistItem.position },
|
||||||
|
|
||||||
|
optimisticResponse: {
|
||||||
|
__typename: 'Mutation',
|
||||||
|
updateTaskChecklistItemLocation: {
|
||||||
|
__typename: 'UpdateTaskChecklistItemLocationPayload',
|
||||||
|
prevChecklistID,
|
||||||
|
checklistID,
|
||||||
|
checklistItem: {
|
||||||
|
__typename: 'TaskChecklistItem',
|
||||||
|
position: checklistItem.position,
|
||||||
|
id: checklistItem.id,
|
||||||
|
taskChecklistID: checklistID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onTaskNameChange={onTaskNameChange}
|
||||||
|
onTaskDescriptionChange={onTaskDescriptionChange}
|
||||||
|
onToggleTaskComplete={task => {
|
||||||
|
setTaskComplete({ variables: { taskID: task.id, complete: !task.complete } });
|
||||||
|
}}
|
||||||
|
onDeleteTask={onDeleteTask}
|
||||||
|
onChangeItemName={(itemID, itemName) => {
|
||||||
|
updateTaskChecklistItemName({ variables: { taskChecklistItemID: itemID, name: itemName } });
|
||||||
|
}}
|
||||||
|
onCloseModal={() => history.push(projectURL)}
|
||||||
|
onChangeChecklistName={(checklistID, newName) => {
|
||||||
|
updateTaskChecklistName({ variables: { taskChecklistID: checklistID, name: newName } });
|
||||||
|
}}
|
||||||
|
onDeleteItem={(checklistID, itemID) => {
|
||||||
|
deleteTaskChecklistItem({
|
||||||
|
variables: { taskChecklistItemID: itemID },
|
||||||
|
optimisticResponse: {
|
||||||
|
__typename: 'Mutation',
|
||||||
|
deleteTaskChecklistItem: {
|
||||||
|
__typename: 'DeleteTaskChecklistItemPayload',
|
||||||
|
ok: true,
|
||||||
|
taskChecklistItem: {
|
||||||
|
__typename: 'TaskChecklistItem',
|
||||||
|
id: itemID,
|
||||||
|
taskChecklistID: checklistID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onToggleChecklistItem={(itemID, complete) => {
|
||||||
|
setTaskChecklistItemComplete({
|
||||||
|
variables: { taskChecklistItemID: itemID, complete },
|
||||||
|
optimisticResponse: {
|
||||||
|
__typename: 'Mutation',
|
||||||
|
setTaskChecklistItemComplete: {
|
||||||
|
__typename: 'TaskChecklistItem',
|
||||||
|
id: itemID,
|
||||||
|
complete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onAddItem={(taskChecklistID, name, position) => {
|
||||||
|
createTaskChecklistItem({ variables: { taskChecklistID, name, position } });
|
||||||
|
}}
|
||||||
|
onMemberProfile={($targetRef, memberID) => {
|
||||||
|
const member = data.findTask.assigned.find(m => m.id === memberID);
|
||||||
|
if (member) {
|
||||||
|
showPopup(
|
||||||
|
$targetRef,
|
||||||
|
<Popup title={null} onClose={() => { }} tab={0}>
|
||||||
|
<MiniProfile
|
||||||
|
user={member}
|
||||||
|
bio="None"
|
||||||
|
onRemoveFromTask={() => {
|
||||||
|
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popup>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onOpenAddMemberPopup={(task, $targetRef) => {
|
||||||
|
showPopup(
|
||||||
|
$targetRef,
|
||||||
|
<Popup title="Members" tab={0} onClose={() => { }}>
|
||||||
|
<MemberManager
|
||||||
|
availableMembers={availableMembers}
|
||||||
|
activeMembers={data.findTask.assigned}
|
||||||
|
onMemberChange={(member, isActive) => {
|
||||||
|
if (isActive) {
|
||||||
|
assignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||||
|
} else {
|
||||||
|
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popup>,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||||
|
onOpenAddChecklistPopup={(_task, $target) => {
|
||||||
|
showPopup(
|
||||||
|
$target,
|
||||||
|
<Popup
|
||||||
|
title={'Add checklist'}
|
||||||
|
tab={0}
|
||||||
|
onClose={() => {
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CreateChecklistPopup
|
||||||
|
onCreateChecklist={checklistData => {
|
||||||
|
let position = 65535;
|
||||||
|
console.log(data.findTask.checklists);
|
||||||
|
if (data.findTask.checklists) {
|
||||||
|
const [lastChecklist] = data.findTask.checklists.slice(-1);
|
||||||
|
console.log(`lastCheclist ${lastChecklist}`);
|
||||||
|
if (lastChecklist) {
|
||||||
|
position = lastChecklist.position * 2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createTaskChecklist({
|
||||||
|
variables: {
|
||||||
|
taskID: data.findTask.id,
|
||||||
|
name: checklistData.name,
|
||||||
|
position,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popup>,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onDeleteChecklist={($target, checklistID) => {
|
||||||
|
showPopup(
|
||||||
|
$target,
|
||||||
|
<Popup tab={0} title="Delete checklist?" onClose={() => hidePopup()}>
|
||||||
|
<p>Deleting a checklist is permanent and there is no way to get it back.</p>
|
||||||
|
<DeleteChecklistButton
|
||||||
|
color="danger"
|
||||||
|
onClick={() => {
|
||||||
|
deleteTaskChecklist({ variables: { taskChecklistID: checklistID } });
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete Checklist
|
||||||
</DeleteChecklistButton>
|
</DeleteChecklistButton>
|
||||||
</Popup>,
|
</Popup>,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onOpenDueDatePopop={(task, $targetRef) => {
|
onOpenDueDatePopop={(task, $targetRef) => {
|
||||||
showPopup(
|
showPopup(
|
||||||
$targetRef,
|
$targetRef,
|
||||||
<Popup
|
<Popup
|
||||||
title={'Change Due Date'}
|
title={'Change Due Date'}
|
||||||
tab={0}
|
tab={0}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
hidePopup();
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DueDateManager
|
<DueDateManager
|
||||||
task={task}
|
task={task}
|
||||||
onRemoveDueDate={t => {
|
onRemoveDueDate={t => {
|
||||||
updateTaskDueDate({variables: {taskID: t.id, dueDate: null}});
|
updateTaskDueDate({ variables: { taskID: t.id, dueDate: null } });
|
||||||
hidePopup();
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
onDueDateChange={(t, newDueDate) => {
|
onDueDateChange={(t, newDueDate) => {
|
||||||
updateTaskDueDate({variables: {taskID: t.id, dueDate: newDueDate}});
|
updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate } });
|
||||||
hidePopup();
|
hidePopup();
|
||||||
}}
|
}}
|
||||||
onCancel={() => {}}
|
onCancel={() => { }}
|
||||||
/>
|
/>
|
||||||
</Popup>,
|
</Popup>,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
</>
|
||||||
}}
|
);
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Details;
|
export default Details;
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import {action} from '@storybook/addon-actions';
|
||||||
import BaseStyles from 'App/BaseStyles';
|
import BaseStyles from 'App/BaseStyles';
|
||||||
import NormalizeStyles from 'App/NormalizeStyles';
|
import NormalizeStyles from 'App/NormalizeStyles';
|
||||||
import { theme } from 'App/ThemeStyles';
|
import {theme} from 'App/ThemeStyles';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import styled, { ThemeProvider } from 'styled-components';
|
import styled, {ThemeProvider} from 'styled-components';
|
||||||
import Checklist, { ChecklistItem } from '.';
|
import Checklist, {ChecklistItem} from '.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: Checklist,
|
component: Checklist,
|
||||||
title: 'Checklist',
|
title: 'Checklist',
|
||||||
parameters: {
|
parameters: {
|
||||||
backgrounds: [
|
backgrounds: [
|
||||||
{ name: 'gray', value: '#f8f8f8', default: true },
|
{name: 'gray', value: '#f8f8f8', default: true},
|
||||||
{ name: 'white', value: '#ffffff' },
|
{name: 'white', value: '#ffffff'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -138,6 +138,7 @@ export const Default = () => {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
wrapperProps={{}}
|
wrapperProps={{}}
|
||||||
handleProps={{}}
|
handleProps={{}}
|
||||||
|
checklistID='id'
|
||||||
itemID={item.id}
|
itemID={item.id}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
complete={item.complete}
|
complete={item.complete}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, {useState, useRef, useEffect} from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { CheckSquare, Trash, Square, CheckSquareOutline, Clock, Cross, AccountPlus } from 'shared/icons';
|
import {CheckSquare, Trash, Square, CheckSquareOutline, Clock, Cross, AccountPlus} from 'shared/icons';
|
||||||
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
|
import {DragDropContext, Droppable, Draggable, DropResult} from 'react-beautiful-dnd';
|
||||||
import {
|
import {
|
||||||
isPositionChanged,
|
isPositionChanged,
|
||||||
getSortedDraggables,
|
getSortedDraggables,
|
||||||
@ -81,7 +81,7 @@ const ChecklistProgressBar = styled.div`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
const ChecklistProgressBarCurrent = styled.div<{ width: number }>`
|
const ChecklistProgressBarCurrent = styled.div<{width: number}>`
|
||||||
width: ${props => props.width}%;
|
width: ${props => props.width}%;
|
||||||
background: rgba(${props => (props.width === 100 ? props.theme.colors.success : props.theme.colors.primary)});
|
background: rgba(${props => (props.width === 100 ? props.theme.colors.success : props.theme.colors.primary)});
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -129,9 +129,10 @@ const ChecklistItemTextControls = styled.div`
|
|||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemText = styled.span<{ complete: boolean }>`
|
const ChecklistItemText = styled.span<{complete: boolean}>`
|
||||||
color: ${props => (props.complete ? '#5e6c84' : `rgba(${props.theme.colors.text.primary})`)};
|
color: ${props => (props.complete ? '#5e6c84' : `rgba(${props.theme.colors.text.primary})`)};
|
||||||
${props => props.complete && 'text-decoration: line-through;'}
|
${props => props.complete && 'text-decoration: line-through;'}
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
@ -155,6 +156,11 @@ const ControlButton = styled.div`
|
|||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.8);
|
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.8);
|
||||||
|
display: flex;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(${props => props.theme.colors.primary}, 1);
|
background-color: rgba(${props => props.theme.colors.primary}, 1);
|
||||||
}
|
}
|
||||||
@ -206,7 +212,7 @@ const TrashButton = styled(Trash)`
|
|||||||
fill: rgba(${props => props.theme.colors.text.primary});
|
fill: rgba(${props => props.theme.colors.text.primary});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ChecklistItemWrapper = styled.div<{ ref: any }>`
|
const ChecklistItemWrapper = styled.div<{ref: any}>`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
clear: both;
|
clear: both;
|
||||||
padding-left: 40px;
|
padding-left: 40px;
|
||||||
@ -216,6 +222,9 @@ const ChecklistItemWrapper = styled.div<{ ref: any }>`
|
|||||||
transition-property: transform, opacity, height, padding, margin;
|
transition-property: transform, opacity, height, padding, margin;
|
||||||
transition-duration: 0.14s;
|
transition-duration: 0.14s;
|
||||||
transition-timing-function: ease-in;
|
transition-timing-function: ease-in;
|
||||||
|
& ${ControlButton}:last-child {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
||||||
@ -274,18 +283,19 @@ const ChecklistNewItem = styled.div`
|
|||||||
|
|
||||||
type ChecklistItemProps = {
|
type ChecklistItemProps = {
|
||||||
itemID: string;
|
itemID: string;
|
||||||
|
checklistID: string;
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
onChangeName: (itemID: string, currentName: string) => void;
|
onChangeName: (itemID: string, currentName: string) => void;
|
||||||
wrapperProps: any;
|
wrapperProps: any;
|
||||||
handleProps: any;
|
handleProps: any;
|
||||||
onToggleItem: (itemID: string, complete: boolean) => void;
|
onToggleItem: (itemID: string, complete: boolean) => void;
|
||||||
onDeleteItem: (itemID: string) => void;
|
onDeleteItem: (checklistIDID: string, itemID: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChecklistItem = React.forwardRef(
|
export const ChecklistItem = React.forwardRef(
|
||||||
(
|
(
|
||||||
{ itemID, complete, name, wrapperProps, handleProps, onChangeName, onToggleItem, onDeleteItem }: ChecklistItemProps,
|
{itemID, checklistID, complete, name, wrapperProps, handleProps, onChangeName, onToggleItem, onDeleteItem}: ChecklistItemProps,
|
||||||
$item,
|
$item,
|
||||||
) => {
|
) => {
|
||||||
const $editor = useRef<HTMLTextAreaElement>(null);
|
const $editor = useRef<HTMLTextAreaElement>(null);
|
||||||
@ -309,8 +319,8 @@ export const ChecklistItem = React.forwardRef(
|
|||||||
{complete ? (
|
{complete ? (
|
||||||
<ChecklistItemCheckedIcon width={20} height={20} />
|
<ChecklistItemCheckedIcon width={20} height={20} />
|
||||||
) : (
|
) : (
|
||||||
<ChecklistItemUncheckedIcon width={20} height={20} />
|
<ChecklistItemUncheckedIcon width={20} height={20} />
|
||||||
)}
|
)}
|
||||||
</ChecklistIcon>
|
</ChecklistIcon>
|
||||||
{editting ? (
|
{editting ? (
|
||||||
<>
|
<>
|
||||||
@ -352,7 +362,7 @@ export const ChecklistItem = React.forwardRef(
|
|||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setEditting(false);
|
setEditting(false);
|
||||||
onDeleteItem(itemID);
|
onDeleteItem(checklistID, itemID);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash width={16} height={16} />
|
<Trash width={16} height={16} />
|
||||||
@ -360,34 +370,34 @@ export const ChecklistItem = React.forwardRef(
|
|||||||
</EditControls>
|
</EditControls>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ChecklistItemDetails
|
<ChecklistItemDetails
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditting(true);
|
setEditting(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ChecklistItemRow>
|
<ChecklistItemRow>
|
||||||
<ChecklistItemTextControls>
|
<ChecklistItemTextControls>
|
||||||
<ChecklistItemText complete={complete}>{name}</ChecklistItemText>
|
<ChecklistItemText complete={complete}>{name}</ChecklistItemText>
|
||||||
<ChecklistControls>
|
<ChecklistControls>
|
||||||
<ControlButton>
|
<ControlButton>
|
||||||
<AssignUserButton width={14} height={14} />
|
<AssignUserButton width={14} height={14} />
|
||||||
</ControlButton>
|
</ControlButton>
|
||||||
<ControlButton>
|
<ControlButton>
|
||||||
<ClockButton width={14} height={14} />
|
<ClockButton width={14} height={14} />
|
||||||
</ControlButton>
|
</ControlButton>
|
||||||
<ControlButton
|
<ControlButton
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onDeleteItem(itemID);
|
onDeleteItem(checklistID, itemID);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TrashButton width={14} height={14} />
|
<TrashButton width={14} height={14} />
|
||||||
</ControlButton>
|
</ControlButton>
|
||||||
</ChecklistControls>
|
</ChecklistControls>
|
||||||
</ChecklistItemTextControls>
|
</ChecklistItemTextControls>
|
||||||
</ChecklistItemRow>
|
</ChecklistItemRow>
|
||||||
</ChecklistItemDetails>
|
</ChecklistItemDetails>
|
||||||
)}
|
)}
|
||||||
</ChecklistItemWrapper>
|
</ChecklistItemWrapper>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -397,7 +407,7 @@ type AddNewItemProps = {
|
|||||||
onAddItem: (name: string) => void;
|
onAddItem: (name: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddNewItem: React.FC<AddNewItemProps> = ({ onAddItem }) => {
|
const AddNewItem: React.FC<AddNewItemProps> = ({onAddItem}) => {
|
||||||
const $editor = useRef<HTMLTextAreaElement>(null);
|
const $editor = useRef<HTMLTextAreaElement>(null);
|
||||||
const $wrapper = useRef<HTMLDivElement>(null);
|
const $wrapper = useRef<HTMLDivElement>(null);
|
||||||
const [currentName, setCurrentName] = useState('');
|
const [currentName, setCurrentName] = useState('');
|
||||||
@ -454,8 +464,8 @@ const AddNewItem: React.FC<AddNewItemProps> = ({ onAddItem }) => {
|
|||||||
</EditControls>
|
</EditControls>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<NewItemButton onClick={() => setEditting(true)}>Add an item</NewItemButton>
|
<NewItemButton onClick={() => setEditting(true)}>Add an item</NewItemButton>
|
||||||
)}
|
)}
|
||||||
</ChecklistNewItem>
|
</ChecklistNewItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -467,7 +477,7 @@ type ChecklistTitleEditorProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ChecklistTitleEditor = React.forwardRef(
|
const ChecklistTitleEditor = React.forwardRef(
|
||||||
({ name, onChangeName, onCancel }: ChecklistTitleEditorProps, $name: any) => {
|
({name, onChangeName, onCancel}: ChecklistTitleEditorProps, $name: any) => {
|
||||||
const [currentName, setCurrentName] = useState(name);
|
const [currentName, setCurrentName] = useState(name);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -515,7 +525,7 @@ type ChecklistProps = {
|
|||||||
onChangeItemName: (itemID: string, currentName: string) => void;
|
onChangeItemName: (itemID: string, currentName: string) => void;
|
||||||
wrapperProps: any;
|
wrapperProps: any;
|
||||||
handleProps: any;
|
handleProps: any;
|
||||||
onDeleteItem: (itemID: string) => void;
|
onDeleteItem: (checklistID: string, itemID: string) => void;
|
||||||
onAddItem: (itemName: string) => void;
|
onAddItem: (itemName: string) => void;
|
||||||
items: Array<TaskChecklistItem>;
|
items: Array<TaskChecklistItem>;
|
||||||
};
|
};
|
||||||
@ -569,21 +579,21 @@ const Checklist = React.forwardRef(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<WindowChecklistTitle {...handleProps}>
|
<WindowChecklistTitle {...handleProps}>
|
||||||
<WindowTitleText onClick={() => setEditting(true)}>{name}</WindowTitleText>
|
<WindowTitleText onClick={() => setEditting(true)}>{name}</WindowTitleText>
|
||||||
<WindowOptions>
|
<WindowOptions>
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
onClick={$target => {
|
onClick={$target => {
|
||||||
onDeleteChecklist($target, checklistID);
|
onDeleteChecklist($target, checklistID);
|
||||||
}}
|
}}
|
||||||
color="danger"
|
color="danger"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
</WindowOptions>
|
</WindowOptions>
|
||||||
</WindowChecklistTitle>
|
</WindowChecklistTitle>
|
||||||
)}
|
)}
|
||||||
</WindowTitle>
|
</WindowTitle>
|
||||||
<ChecklistProgress>
|
<ChecklistProgress>
|
||||||
<ChecklistProgressPercent>{`${percent}%`}</ChecklistProgressPercent>
|
<ChecklistProgressPercent>{`${percent}%`}</ChecklistProgressPercent>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, {useState, useRef, useEffect} from 'react';
|
||||||
import { Bin, Cross, Plus } from 'shared/icons';
|
import {Bin, Cross, Plus} from 'shared/icons';
|
||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
getNewDraggablePosition,
|
getNewDraggablePosition,
|
||||||
getAfterDropDraggableList,
|
getAfterDropDraggableList,
|
||||||
} from 'shared/utils/draggables';
|
} from 'shared/utils/draggables';
|
||||||
import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd';
|
import {DragDropContext, Droppable, Draggable, DropResult} from 'react-beautiful-dnd';
|
||||||
import TaskAssignee from 'shared/components/TaskAssignee';
|
import TaskAssignee from 'shared/components/TaskAssignee';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ import {
|
|||||||
MetaDetailTitle,
|
MetaDetailTitle,
|
||||||
MetaDetailContent,
|
MetaDetailContent,
|
||||||
} from './Styles';
|
} from './Styles';
|
||||||
import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist';
|
import Checklist, {ChecklistItem, ChecklistItems} from '../Checklist';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const ChecklistContainer = styled.div``;
|
const ChecklistContainer = styled.div``;
|
||||||
@ -69,7 +69,7 @@ type TaskLabelProps = {
|
|||||||
onClick: ($target: React.RefObject<HTMLElement>) => void;
|
onClick: ($target: React.RefObject<HTMLElement>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TaskLabelItem: React.FC<TaskLabelProps> = ({ label, onClick }) => {
|
const TaskLabelItem: React.FC<TaskLabelProps> = ({label, onClick}) => {
|
||||||
const $label = useRef<HTMLDivElement>(null);
|
const $label = useRef<HTMLDivElement>(null);
|
||||||
return (
|
return (
|
||||||
<TaskDetailLabel
|
<TaskDetailLabel
|
||||||
@ -84,14 +84,14 @@ const TaskLabelItem: React.FC<TaskLabelProps> = ({ label, onClick }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TaskContent: React.FC<TaskContentProps> = ({ description, onEditContent }) => {
|
const TaskContent: React.FC<TaskContentProps> = ({description, onEditContent}) => {
|
||||||
return description === '' ? (
|
return description === '' ? (
|
||||||
<TaskDetailsAddDetailsButton onClick={onEditContent}>Add a more detailed description</TaskDetailsAddDetailsButton>
|
<TaskDetailsAddDetailsButton onClick={onEditContent}>Add a more detailed description</TaskDetailsAddDetailsButton>
|
||||||
) : (
|
) : (
|
||||||
<TaskDetailsMarkdown onClick={onEditContent}>
|
<TaskDetailsMarkdown onClick={onEditContent}>
|
||||||
<ReactMarkdown source={description} />
|
<ReactMarkdown source={description} />
|
||||||
</TaskDetailsMarkdown>
|
</TaskDetailsMarkdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type DetailsEditorProps = {
|
type DetailsEditorProps = {
|
||||||
@ -144,7 +144,7 @@ type TaskDetailsProps = {
|
|||||||
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
||||||
onDeleteTask: (task: Task) => void;
|
onDeleteTask: (task: Task) => void;
|
||||||
onAddItem: (checklistID: string, name: string, position: number) => void;
|
onAddItem: (checklistID: string, name: string, position: number) => void;
|
||||||
onDeleteItem: (itemID: string) => void;
|
onDeleteItem: (checklistID: string, itemID: string) => void;
|
||||||
onChangeItemName: (itemID: string, itemName: string) => void;
|
onChangeItemName: (itemID: string, itemName: string) => void;
|
||||||
onToggleTaskComplete: (task: Task) => void;
|
onToggleTaskComplete: (task: Task) => void;
|
||||||
onToggleChecklistItem: (itemID: string, complete: boolean) => void;
|
onToggleChecklistItem: (itemID: string, complete: boolean) => void;
|
||||||
@ -214,7 +214,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
onOpenAddLabelPopup(task, $target);
|
onOpenAddLabelPopup(task, $target);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => {
|
const onDragEnd = ({draggableId, source, destination, type}: DropResult) => {
|
||||||
if (typeof destination === 'undefined') return;
|
if (typeof destination === 'undefined') return;
|
||||||
if (!isPositionChanged(source, destination)) return;
|
if (!isPositionChanged(source, destination)) return;
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
beforeDropDraggables = getSortedDraggables(
|
beforeDropDraggables = getSortedDraggables(
|
||||||
task.checklists.map(checklist => {
|
task.checklists.map(checklist => {
|
||||||
return { id: checklist.id, position: checklist.position };
|
return {id: checklist.id, position: checklist.position};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (droppedDraggable === null || beforeDropDraggables === null) {
|
if (droppedDraggable === null || beforeDropDraggables === null) {
|
||||||
@ -249,9 +249,9 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
||||||
console.log(droppedGroup);
|
console.log(droppedGroup);
|
||||||
console.log(`positiion: ${newPosition}`);
|
console.log(`positiion: ${newPosition}`);
|
||||||
onChecklistDrop({ ...droppedGroup, position: newPosition });
|
onChecklistDrop({...droppedGroup, position: newPosition});
|
||||||
} else {
|
} else {
|
||||||
throw { error: 'task group can not be found' };
|
throw {error: 'task group can not be found'};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const targetChecklist = task.checklists.findIndex(
|
const targetChecklist = task.checklists.findIndex(
|
||||||
@ -266,7 +266,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
};
|
};
|
||||||
beforeDropDraggables = getSortedDraggables(
|
beforeDropDraggables = getSortedDraggables(
|
||||||
task.checklists[targetChecklist].items.map(item => {
|
task.checklists[targetChecklist].items.map(item => {
|
||||||
return { id: item.id, position: item.position };
|
return {id: item.id, position: item.position};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (droppedDraggable === null || beforeDropDraggables === null) {
|
if (droppedDraggable === null || beforeDropDraggables === null) {
|
||||||
@ -379,8 +379,8 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TaskContent description={description} onEditContent={handleClick} />
|
<TaskContent description={description} onEditContent={handleClick} />
|
||||||
)}
|
)}
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<Droppable direction="vertical" type="checklist" droppableId="root">
|
<Droppable direction="vertical" type="checklist" droppableId="root">
|
||||||
{dropProvided => (
|
{dropProvided => (
|
||||||
@ -430,6 +430,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
<ChecklistItem
|
<ChecklistItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
itemID={item.id}
|
itemID={item.id}
|
||||||
|
checklistID={item.taskChecklistID}
|
||||||
ref={itemDrop.innerRef}
|
ref={itemDrop.innerRef}
|
||||||
wrapperProps={itemDrop.draggableProps}
|
wrapperProps={itemDrop.draggableProps}
|
||||||
handleProps={itemDrop.dragHandleProps}
|
handleProps={itemDrop.dragHandleProps}
|
||||||
@ -437,7 +438,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
complete={item.complete}
|
complete={item.complete}
|
||||||
onDeleteItem={onDeleteItem}
|
onDeleteItem={onDeleteItem}
|
||||||
onChangeName={onChangeItemName}
|
onChangeName={onChangeItemName}
|
||||||
onToggleItem={() => {}}
|
onToggleItem={(itemID, complete) => onToggleChecklistItem(item.id, complete)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
Loading…
Reference in New Issue
Block a user