feat: drag and drop an issue to delete (#270)

* feat: drag and drop an issue to delete

* style: repositioned trash box
This commit is contained in:
Aaryan Khandelwal 2023-02-13 10:32:02 +05:30 committed by GitHub
parent 0a88b3ed84
commit d0afa486c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 447 additions and 378 deletions

View File

@ -1,5 +1,3 @@
// react-beautiful-dnd
import { DragDropContext, DropResult } from "react-beautiful-dnd";
// hooks // hooks
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// components // components
@ -15,7 +13,7 @@ type Props = {
addIssueToState: (groupTitle: string, stateId: string | null) => void; addIssueToState: (groupTitle: string, stateId: string | null) => void;
openIssuesListModal?: (() => void) | null; openIssuesListModal?: (() => void) | null;
handleDeleteIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void;
handleOnDragEnd: (result: DropResult) => void; handleTrashBox: (isDragging: boolean) => void;
userAuth: UserAuth; userAuth: UserAuth;
}; };
@ -27,7 +25,7 @@ export const AllBoards: React.FC<Props> = ({
addIssueToState, addIssueToState,
openIssuesListModal, openIssuesListModal,
handleDeleteIssue, handleDeleteIssue,
handleOnDragEnd, handleTrashBox,
userAuth, userAuth,
}) => { }) => {
const { groupedByIssues, groupByProperty: selectedGroup, orderBy } = useIssueView(issues); const { groupedByIssues, groupByProperty: selectedGroup, orderBy } = useIssueView(issues);
@ -36,42 +34,41 @@ export const AllBoards: React.FC<Props> = ({
<> <>
{groupedByIssues ? ( {groupedByIssues ? (
<div className="h-[calc(100vh-157px)] lg:h-[calc(100vh-115px)] w-full"> <div className="h-[calc(100vh-157px)] lg:h-[calc(100vh-115px)] w-full">
<DragDropContext onDragEnd={handleOnDragEnd}> <div className="h-full w-full overflow-hidden">
<div className="h-full w-full overflow-hidden"> <div className="h-full w-full">
<div className="h-full w-full"> <div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden"> {Object.keys(groupedByIssues).map((singleGroup, index) => {
{Object.keys(groupedByIssues).map((singleGroup, index) => { const stateId =
const stateId = selectedGroup === "state_detail.name"
selectedGroup === "state_detail.name" ? states?.find((s) => s.name === singleGroup)?.id ?? null
? states?.find((s) => s.name === singleGroup)?.id ?? null : null;
: null;
const bgColor = const bgColor =
selectedGroup === "state_detail.name" selectedGroup === "state_detail.name"
? states?.find((s) => s.name === singleGroup)?.color ? states?.find((s) => s.name === singleGroup)?.color
: "#000000"; : "#000000";
return ( return (
<SingleBoard <SingleBoard
key={index} key={index}
type={type} type={type}
bgColor={bgColor} bgColor={bgColor}
groupTitle={singleGroup} groupTitle={singleGroup}
groupedByIssues={groupedByIssues} groupedByIssues={groupedByIssues}
selectedGroup={selectedGroup} selectedGroup={selectedGroup}
members={members} members={members}
addIssueToState={() => addIssueToState(singleGroup, stateId)} addIssueToState={() => addIssueToState(singleGroup, stateId)}
handleDeleteIssue={handleDeleteIssue} handleDeleteIssue={handleDeleteIssue}
openIssuesListModal={openIssuesListModal ?? null} openIssuesListModal={openIssuesListModal ?? null}
orderBy={orderBy} orderBy={orderBy}
userAuth={userAuth} handleTrashBox={handleTrashBox}
/> userAuth={userAuth}
); />
})} );
</div> })}
</div> </div>
</div> </div>
</DragDropContext> </div>
</div> </div>
) : ( ) : (
<div className="flex h-full w-full items-center justify-center">Loading...</div> <div className="flex h-full w-full items-center justify-center">Loading...</div>

View File

@ -29,6 +29,7 @@ type Props = {
handleDeleteIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void;
openIssuesListModal?: (() => void) | null; openIssuesListModal?: (() => void) | null;
orderBy: NestedKeyOf<IIssue> | "manual" | null; orderBy: NestedKeyOf<IIssue> | "manual" | null;
handleTrashBox: (isDragging: boolean) => void;
userAuth: UserAuth; userAuth: UserAuth;
}; };
@ -43,6 +44,7 @@ export const SingleBoard: React.FC<Props> = ({
handleDeleteIssue, handleDeleteIssue,
openIssuesListModal, openIssuesListModal,
orderBy, orderBy,
handleTrashBox,
userAuth, userAuth,
}) => { }) => {
// collapse/expand // collapse/expand
@ -89,17 +91,27 @@ export const SingleBoard: React.FC<Props> = ({
{...provided.droppableProps} {...provided.droppableProps}
> >
{groupedByIssues[groupTitle].map((issue, index: number) => ( {groupedByIssues[groupTitle].map((issue, index: number) => (
<SingleBoardIssue <Draggable
key={index} key={issue.id}
draggableId={issue.id}
index={index} index={index}
type={type} isDragDisabled={selectedGroup === "created_by"}
issue={issue} >
selectedGroup={selectedGroup} {(provided, snapshot) => (
properties={properties} <SingleBoardIssue
handleDeleteIssue={handleDeleteIssue} key={index}
orderBy={orderBy} provided={provided}
userAuth={userAuth} snapshot={snapshot}
/> type={type}
issue={issue}
properties={properties}
handleDeleteIssue={handleDeleteIssue}
orderBy={orderBy}
handleTrashBox={handleTrashBox}
userAuth={userAuth}
/>
)}
</Draggable>
))} ))}
<span <span
style={{ style={{

View File

@ -1,13 +1,13 @@
import React, { useCallback } from "react"; import React, { useCallback, useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { import {
Draggable, DraggableProvided,
DraggableStateSnapshot, DraggableStateSnapshot,
DraggingStyle, DraggingStyle,
NotDraggingStyle, NotDraggingStyle,
@ -37,24 +37,26 @@ import {
import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST } from "constants/fetch-keys"; import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
type Props = { type Props = {
index: number;
type?: string; type?: string;
provided: DraggableProvided;
snapshot: DraggableStateSnapshot;
issue: IIssue; issue: IIssue;
selectedGroup: NestedKeyOf<IIssue> | null;
properties: Properties; properties: Properties;
handleDeleteIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void;
orderBy: NestedKeyOf<IIssue> | "manual" | null; orderBy: NestedKeyOf<IIssue> | "manual" | null;
handleTrashBox: (isDragging: boolean) => void;
userAuth: UserAuth; userAuth: UserAuth;
}; };
export const SingleBoardIssue: React.FC<Props> = ({ export const SingleBoardIssue: React.FC<Props> = ({
index,
type, type,
provided,
snapshot,
issue, issue,
selectedGroup,
properties, properties,
handleDeleteIssue, handleDeleteIssue,
orderBy, orderBy,
handleTrashBox,
userAuth, userAuth,
}) => { }) => {
const router = useRouter(); const router = useRouter();
@ -151,90 +153,84 @@ export const SingleBoardIssue: React.FC<Props> = ({
const isNotAllowed = userAuth.isGuest || userAuth.isViewer; const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
useEffect(() => {
if (snapshot.isDragging) handleTrashBox(snapshot.isDragging);
}, [snapshot, handleTrashBox]);
return ( return (
<Draggable <div
key={issue.id} className={`rounded border bg-white shadow-sm ${
draggableId={issue.id} snapshot.isDragging ? "border-theme bg-indigo-50 shadow-lg" : ""
index={index} }`}
isDragDisabled={selectedGroup === "created_by"} ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getStyle(provided.draggableProps.style, snapshot)}
> >
{(provided, snapshot) => ( <div className="group/card relative select-none p-2">
<div {!isNotAllowed && (
className={`rounded border bg-white shadow-sm ${ <div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100">
snapshot.isDragging ? "border-theme bg-indigo-50 shadow-lg" : "" <button
}`} type="button"
ref={provided.innerRef} className="grid h-7 w-7 place-items-center rounded bg-white p-1 text-red-500 outline-none duration-300 hover:bg-red-50"
{...provided.draggableProps} onClick={() => handleDeleteIssue(issue)}
{...provided.dragHandleProps} >
style={getStyle(provided.draggableProps.style, snapshot)} <TrashIcon className="h-4 w-4" />
> </button>
<div className="group/card relative select-none p-2"> </div>
{!isNotAllowed && ( )}
<div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100"> <Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}>
<button <a>
type="button" {properties.key && (
className="grid h-7 w-7 place-items-center rounded bg-white p-1 text-red-500 outline-none duration-300 hover:bg-red-50" <div className="mb-2 text-xs font-medium text-gray-500">
onClick={() => handleDeleteIssue(issue)} {issue.project_detail.identifier}-{issue.sequence_id}
>
<TrashIcon className="h-4 w-4" />
</button>
</div> </div>
)} )}
<Link href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}> <h5
<a> className="mb-3 text-sm group-hover:text-theme"
{properties.key && ( style={{ lineClamp: 3, WebkitLineClamp: 3 }}
<div className="mb-2 text-xs font-medium text-gray-500"> >
{issue.project_detail.identifier}-{issue.sequence_id} {issue.name}
</div> </h5>
)} </a>
<h5 </Link>
className="mb-3 text-sm group-hover:text-theme" <div className="flex flex-wrap items-center gap-x-1 gap-y-2 text-xs">
style={{ lineClamp: 3, WebkitLineClamp: 3 }} {properties.priority && (
> <ViewPrioritySelect
{issue.name} issue={issue}
</h5> partialUpdateIssue={partialUpdateIssue}
</a> isNotAllowed={isNotAllowed}
</Link> position="left"
<div className="flex flex-wrap items-center gap-x-1 gap-y-2 text-xs"> />
{properties.priority && ( )}
<ViewPrioritySelect {properties.state && (
issue={issue} <ViewStateSelect
partialUpdateIssue={partialUpdateIssue} issue={issue}
isNotAllowed={isNotAllowed} partialUpdateIssue={partialUpdateIssue}
position="left" isNotAllowed={isNotAllowed}
/> />
)} )}
{properties.state && ( {properties.due_date && (
<ViewStateSelect <ViewDueDateSelect
issue={issue} issue={issue}
partialUpdateIssue={partialUpdateIssue} partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed} isNotAllowed={isNotAllowed}
/> />
)} )}
{properties.due_date && ( {properties.sub_issue_count && (
<ViewDueDateSelect <div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
issue={issue} {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
/>
)}
{properties.sub_issue_count && (
<div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
{issue.sub_issues_count}{" "}
{issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
</div>
)}
{properties.assignee && (
<ViewAssigneeSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
/>
)}
</div> </div>
</div> )}
{properties.assignee && (
<ViewAssigneeSelect
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
/>
)}
</div> </div>
)} </div>
</Draggable> </div>
); );
}; };

View File

@ -5,7 +5,7 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DropResult } from "react-beautiful-dnd"; import { DragDropContext, DropResult } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
import stateService from "services/state.service"; import stateService from "services/state.service";
@ -16,6 +16,9 @@ import useIssueView from "hooks/use-issue-view";
// components // components
import { AllLists, AllBoards } from "components/core"; import { AllLists, AllBoards } from "components/core";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
// icons
import { TrashIcon } from "@heroicons/react/24/outline";
// helpers // helpers
import { getStatesList } from "helpers/state.helper"; import { getStatesList } from "helpers/state.helper";
// types // types
@ -58,6 +61,9 @@ export const IssuesView: React.FC<Props> = ({
const [deleteIssueModal, setDeleteIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const [issueToDelete, setIssueToDelete] = useState<IIssue | null>(null); const [issueToDelete, setIssueToDelete] = useState<IIssue | null>(null);
// trash box
const [trashBox, setTrashBox] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
@ -78,271 +84,308 @@ export const IssuesView: React.FC<Props> = ({
: null : null
); );
const handleDeleteIssue = useCallback(
(issue: IIssue) => {
setDeleteIssueModal(true);
setIssueToDelete(issue);
},
[setDeleteIssueModal, setIssueToDelete]
);
const handleOnDragEnd = useCallback( const handleOnDragEnd = useCallback(
(result: DropResult) => { (result: DropResult) => {
setTrashBox(false);
if (!result.destination || !workspaceSlug || !projectId) return; if (!result.destination || !workspaceSlug || !projectId) return;
const { source, destination } = result; const { source, destination } = result;
const draggedItem = groupedByIssues[source.droppableId][source.index]; const draggedItem = groupedByIssues[source.droppableId][source.index];
if (source.droppableId !== destination.droppableId) { if (destination.droppableId === "trashBox") {
const sourceGroup = source.droppableId; // source group id handleDeleteIssue(draggedItem);
const destinationGroup = destination.droppableId; // destination group id } else {
if (source.droppableId !== destination.droppableId) {
const sourceGroup = source.droppableId; // source group id
const destinationGroup = destination.droppableId; // destination group id
if (!sourceGroup || !destinationGroup) return; if (!sourceGroup || !destinationGroup) return;
if (selectedGroup === "priority") { if (selectedGroup === "priority") {
// update the removed item for mutation // update the removed item for mutation
draggedItem.priority = destinationGroup; draggedItem.priority = destinationGroup;
if (cycleId) if (cycleId)
mutate<CycleIssueResponse[]>( mutate<CycleIssueResponse[]>(
CYCLE_ISSUES(cycleId as string), CYCLE_ISSUES(cycleId as string),
(prevData) => {
if (!prevData) return prevData;
const updatedIssues = prevData.map((issue) => {
if (issue.issue_detail.id === draggedItem.id) {
return {
...issue,
issue_detail: {
...draggedItem,
priority: destinationGroup,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
if (moduleId)
mutate<ModuleIssueResponse[]>(
MODULE_ISSUES(moduleId as string),
(prevData) => {
if (!prevData) return prevData;
const updatedIssues = prevData.map((issue) => {
if (issue.issue_detail.id === draggedItem.id) {
return {
...issue,
issue_detail: {
...draggedItem,
priority: destinationGroup,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
mutate<IssueResponse>(
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
(prevData) => { (prevData) => {
if (!prevData) return prevData; if (!prevData) return prevData;
const updatedIssues = prevData.map((issue) => {
if (issue.issue_detail.id === draggedItem.id) { const updatedIssues = prevData.results.map((issue) => {
if (issue.id === draggedItem.id)
return { return {
...issue, ...draggedItem,
issue_detail: { priority: destinationGroup,
...draggedItem,
priority: destinationGroup,
},
}; };
}
return issue; return issue;
}); });
return [...updatedIssues];
return {
...prevData,
results: updatedIssues,
};
}, },
false false
); );
if (moduleId) // patch request
mutate<ModuleIssueResponse[]>( issuesService
MODULE_ISSUES(moduleId as string), .patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, {
(prevData) => { priority: destinationGroup,
if (!prevData) return prevData; })
const updatedIssues = prevData.map((issue) => { .then((res) => {
if (issue.issue_detail.id === draggedItem.id) { if (cycleId) mutate(CYCLE_ISSUES(cycleId as string));
return { if (moduleId) mutate(MODULE_ISSUES(moduleId as string));
...issue,
issue_detail: {
...draggedItem,
priority: destinationGroup,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
mutate<IssueResponse>( mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string));
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
(prevData) => {
if (!prevData) return prevData;
const updatedIssues = prevData.results.map((issue) => {
if (issue.id === draggedItem.id)
return {
...draggedItem,
priority: destinationGroup,
};
return issue;
}); });
} else if (selectedGroup === "state_detail.name") {
const destinationState = states?.find((s) => s.name === destinationGroup);
const destinationStateId = destinationState?.id;
return { // update the removed item for mutation
...prevData, if (!destinationStateId || !destinationState) return;
results: updatedIssues, draggedItem.state = destinationStateId;
}; draggedItem.state_detail = destinationState;
},
false
);
// patch request if (cycleId)
issuesService mutate<CycleIssueResponse[]>(
.patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, { CYCLE_ISSUES(cycleId as string),
priority: destinationGroup, (prevData) => {
}) if (!prevData) return prevData;
.then((res) => { const updatedIssues = prevData.map((issue) => {
if (cycleId) mutate(CYCLE_ISSUES(cycleId as string)); if (issue.issue_detail.id === draggedItem.id) {
if (moduleId) mutate(MODULE_ISSUES(moduleId as string)); return {
...issue,
issue_detail: {
...draggedItem,
state_detail: destinationState,
state: destinationStateId,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); if (moduleId)
}); mutate<ModuleIssueResponse[]>(
} else if (selectedGroup === "state_detail.name") { MODULE_ISSUES(moduleId as string),
const destinationState = states?.find((s) => s.name === destinationGroup); (prevData) => {
const destinationStateId = destinationState?.id; if (!prevData) return prevData;
const updatedIssues = prevData.map((issue) => {
if (issue.issue_detail.id === draggedItem.id) {
return {
...issue,
issue_detail: {
...draggedItem,
state_detail: destinationState,
state: destinationStateId,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
// update the removed item for mutation mutate<IssueResponse>(
if (!destinationStateId || !destinationState) return; PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
draggedItem.state = destinationStateId;
draggedItem.state_detail = destinationState;
if (cycleId)
mutate<CycleIssueResponse[]>(
CYCLE_ISSUES(cycleId as string),
(prevData) => { (prevData) => {
if (!prevData) return prevData; if (!prevData) return prevData;
const updatedIssues = prevData.map((issue) => {
if (issue.issue_detail.id === draggedItem.id) { const updatedIssues = prevData.results.map((issue) => {
if (issue.id === draggedItem.id)
return { return {
...issue, ...draggedItem,
issue_detail: { state_detail: destinationState,
...draggedItem, state: destinationStateId,
state_detail: destinationState,
state: destinationStateId,
},
}; };
}
return issue; return issue;
}); });
return [...updatedIssues];
return {
...prevData,
results: updatedIssues,
};
}, },
false false
); );
if (moduleId) // patch request
mutate<ModuleIssueResponse[]>( issuesService
MODULE_ISSUES(moduleId as string), .patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, {
(prevData) => { state: destinationStateId,
if (!prevData) return prevData; })
const updatedIssues = prevData.map((issue) => { .then((res) => {
if (issue.issue_detail.id === draggedItem.id) { if (cycleId) mutate(CYCLE_ISSUES(cycleId as string));
return { if (moduleId) mutate(MODULE_ISSUES(moduleId as string));
...issue,
issue_detail: {
...draggedItem,
state_detail: destinationState,
state: destinationStateId,
},
};
}
return issue;
});
return [...updatedIssues];
},
false
);
mutate<IssueResponse>( mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string));
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
(prevData) => {
if (!prevData) return prevData;
const updatedIssues = prevData.results.map((issue) => {
if (issue.id === draggedItem.id)
return {
...draggedItem,
state_detail: destinationState,
state: destinationStateId,
};
return issue;
}); });
}
return {
...prevData,
results: updatedIssues,
};
},
false
);
// patch request
issuesService
.patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, {
state: destinationStateId,
})
.then((res) => {
if (cycleId) mutate(CYCLE_ISSUES(cycleId as string));
if (moduleId) mutate(MODULE_ISSUES(moduleId as string));
mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string));
});
} }
} }
}, },
[workspaceSlug, cycleId, moduleId, groupedByIssues, projectId, selectedGroup, states] [
workspaceSlug,
cycleId,
moduleId,
groupedByIssues,
projectId,
selectedGroup,
states,
handleDeleteIssue,
]
); );
const addIssueToState = (groupTitle: string, stateId: string | null) => { const addIssueToState = useCallback(
setCreateIssueModal(true); (groupTitle: string, stateId: string | null) => {
if (selectedGroup) setCreateIssueModal(true);
setPreloadedData({ if (selectedGroup)
state: stateId ?? undefined, setPreloadedData({
[selectedGroup]: groupTitle, state: stateId ?? undefined,
actionType: "createIssue", [selectedGroup]: groupTitle,
actionType: "createIssue",
});
else setPreloadedData({ actionType: "createIssue" });
},
[setCreateIssueModal, setPreloadedData, selectedGroup]
);
const handleEditIssue = useCallback(
(issue: IIssue) => {
setEditIssueModal(true);
setIssueToEdit({
...issue,
actionType: "edit",
cycle: issue.issue_cycle ? issue.issue_cycle.cycle : null,
module: issue.issue_module ? issue.issue_module.module : null,
}); });
else setPreloadedData({ actionType: "createIssue" }); },
}; [setEditIssueModal, setIssueToEdit]
);
const handleEditIssue = (issue: IIssue) => { const removeIssueFromCycle = useCallback(
setEditIssueModal(true); (bridgeId: string) => {
setIssueToEdit({ if (!workspaceSlug || !projectId) return;
...issue,
actionType: "edit",
cycle: issue.issue_cycle ? issue.issue_cycle.cycle : null,
module: issue.issue_module ? issue.issue_module.module : null,
});
};
const handleDeleteIssue = (issue: IIssue) => { mutate<CycleIssueResponse[]>(
setDeleteIssueModal(true); CYCLE_ISSUES(cycleId as string),
setIssueToDelete(issue); (prevData) => prevData?.filter((p) => p.id !== bridgeId),
}; false
);
const removeIssueFromCycle = (bridgeId: string) => { issuesService
if (!workspaceSlug || !projectId) return; .removeIssueFromCycle(
workspaceSlug as string,
projectId as string,
cycleId as string,
bridgeId
)
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
},
[workspaceSlug, projectId, cycleId]
);
mutate<CycleIssueResponse[]>( const removeIssueFromModule = useCallback(
CYCLE_ISSUES(cycleId as string), (bridgeId: string) => {
(prevData) => prevData?.filter((p) => p.id !== bridgeId), if (!workspaceSlug || !projectId) return;
false
);
issuesService mutate<ModuleIssueResponse[]>(
.removeIssueFromCycle( MODULE_ISSUES(moduleId as string),
workspaceSlug as string, (prevData) => prevData?.filter((p) => p.id !== bridgeId),
projectId as string, false
cycleId as string, );
bridgeId
)
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
};
const removeIssueFromModule = (bridgeId: string) => { modulesService
if (!workspaceSlug || !projectId) return; .removeIssueFromModule(
workspaceSlug as string,
projectId as string,
moduleId as string,
bridgeId
)
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
},
[workspaceSlug, projectId, moduleId]
);
mutate<ModuleIssueResponse[]>( const handleTrashBox = useCallback(
MODULE_ISSUES(moduleId as string), (isDragging: boolean) => {
(prevData) => prevData?.filter((p) => p.id !== bridgeId), if (isDragging && !trashBox) setTrashBox(true);
false },
); [trashBox, setTrashBox]
);
modulesService
.removeIssueFromModule(
workspaceSlug as string,
projectId as string,
moduleId as string,
bridgeId
)
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
};
return ( return (
<> <>
@ -364,38 +407,59 @@ export const IssuesView: React.FC<Props> = ({
isOpen={deleteIssueModal} isOpen={deleteIssueModal}
data={issueToDelete} data={issueToDelete}
/> />
{issueView === "list" ? (
<AllLists <div className="relative">
type={type} <DragDropContext onDragEnd={handleOnDragEnd}>
issues={issues} <StrictModeDroppable droppableId="trashBox">
states={states} {(provided, snapshot) => (
members={members} <div
addIssueToState={addIssueToState} className={`${
handleEditIssue={handleEditIssue} trashBox ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"
handleDeleteIssue={handleDeleteIssue} } fixed z-20 top-12 left-1/2 -translate-x-1/2 flex items-center gap-2 bg-red-100 border-2 border-red-500 p-3 text-xs rounded ${
openIssuesListModal={type !== "issue" ? openIssuesListModal : null} snapshot.isDraggingOver ? "bg-red-500 text-white" : ""
removeIssue={ } duration-200`}
type === "cycle" ref={provided.innerRef}
? removeIssueFromCycle {...provided.droppableProps}
: type === "module" >
? removeIssueFromModule <TrashIcon className="h-3 w-3" />
: null Drop issue here to delete
} </div>
userAuth={userAuth} )}
/> </StrictModeDroppable>
) : ( {issueView === "list" ? (
<AllBoards <AllLists
type={type} type={type}
issues={issues} issues={issues}
states={states} states={states}
members={members} members={members}
addIssueToState={addIssueToState} addIssueToState={addIssueToState}
openIssuesListModal={type !== "issue" ? openIssuesListModal : null} handleEditIssue={handleEditIssue}
handleDeleteIssue={handleDeleteIssue} handleDeleteIssue={handleDeleteIssue}
handleOnDragEnd={handleOnDragEnd} openIssuesListModal={type !== "issue" ? openIssuesListModal : null}
userAuth={userAuth} removeIssue={
/> type === "cycle"
)} ? removeIssueFromCycle
: type === "module"
? removeIssueFromModule
: null
}
userAuth={userAuth}
/>
) : (
<AllBoards
type={type}
issues={issues}
states={states}
members={members}
addIssueToState={addIssueToState}
openIssuesListModal={type !== "issue" ? openIssuesListModal : null}
handleDeleteIssue={handleDeleteIssue}
handleTrashBox={handleTrashBox}
userAuth={userAuth}
/>
)}
</DragDropContext>
</div>
</> </>
); );
}; };