fix: v3 issues for the layouts (#2941)

* fix drag n drop exception error

* fix peek overlay close buttons

* fix project empty state view

* fix cycle and module empty state view

* add ai options to inbox issue creation

* fix inbox filters for viewers

* fix inbox filters for viewers for project

* disable editing permission for members and viewers

* define accurate types for drag and drop
This commit is contained in:
rahulramesha 2023-11-29 19:58:27 +05:30 committed by sriram veeraghanta
parent f7fa4d8b65
commit 90ca459b4a
17 changed files with 290 additions and 147 deletions

View File

@ -16,6 +16,10 @@ import { Button, Input, ToggleSwitch } from "@plane/ui";
// types
import { IIssue } from "types";
import useEditorSuggestions from "hooks/use-editor-suggestions";
import { GptAssistantModal } from "components/core";
import { Sparkle } from "lucide-react";
import useToast from "hooks/use-toast";
import { AIService } from "services/ai.service";
type Props = {
isOpen: boolean;
@ -31,6 +35,7 @@ const defaultValues: Partial<IIssue> = {
};
// services
const aiService = new AIService();
const fileService = new FileService();
export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
@ -38,21 +43,35 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
// states
const [createMore, setCreateMore] = useState(false);
const [gptAssistantModal, setGptAssistantModal] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
const editorRef = useRef<any>(null);
const { setToastAlert } = useToast();
const editorSuggestion = useEditorSuggestions();
const router = useRouter();
const { workspaceSlug, projectId, inboxId } = router.query;
const { workspaceSlug, projectId, inboxId } = router.query as {
workspaceSlug: string;
projectId: string;
inboxId: string;
};
const { inboxIssueDetails: inboxIssueDetailsStore, trackEvent: { postHogEventTracker } } = useMobxStore();
const {
inboxIssueDetails: inboxIssueDetailsStore,
trackEvent: { postHogEventTracker },
appConfig: { envConfig },
} = useMobxStore();
const {
control,
formState: { errors, isSubmitting },
handleSubmit,
reset,
watch,
getValues,
setValue,
} = useForm({ defaultValues });
const handleClose = () => {
@ -60,6 +79,8 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
reset(defaultValues);
};
const issueName = watch("name");
const handleFormSubmit = async (formData: Partial<IIssue>) => {
if (!workspaceSlug || !projectId || !inboxId) return;
@ -70,24 +91,66 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
router.push(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}`);
handleClose();
} else reset(defaultValues);
postHogEventTracker(
"ISSUE_CREATE",
{
...res,
state: "SUCCESS"
}
);
}).catch((error) => {
postHogEventTracker("ISSUE_CREATE", {
...res,
state: "SUCCESS",
});
})
.catch((error) => {
console.log(error);
postHogEventTracker(
"ISSUE_CREATE",
{
state: "FAILED"
}
);
postHogEventTracker("ISSUE_CREATE", {
state: "FAILED",
});
});
};
const handleAiAssistance = async (response: string) => {
if (!workspaceSlug || !projectId) return;
setValue("description", {});
setValue("description_html", `${watch("description_html")}<p>${response}</p>`);
editorRef.current?.setEditorValue(`${watch("description_html")}`);
};
const handleAutoGenerateDescription = async () => {
if (!workspaceSlug || !projectId || !issueName) return;
setIAmFeelingLucky(true);
aiService
.createGptTask(workspaceSlug as string, projectId as string, {
prompt: issueName,
task: "Generate a proper description for this issue.",
})
.then((res) => {
if (res.response === "")
setToastAlert({
type: "error",
title: "Error!",
message:
"Issue title isn't informative enough to generate the description. Please try with a different title.",
});
else handleAiAssistance(res.response_html);
})
.catch((err) => {
const error = err?.data?.error;
if (err.status === 429)
setToastAlert({
type: "error",
title: "Error!",
message: error || "You have reached the maximum number of requests of 50 requests per month per user.",
});
else
setToastAlert({
type: "error",
title: "Error!",
message: error || "Some error occurred. Please try again.",
});
})
.finally(() => setIAmFeelingLucky(false));
};
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-20" onClose={onClose}>
@ -146,7 +209,35 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
)}
/>
</div>
<div>
<div className="relative">
<div className="flex justify-end">
{issueName && issueName !== "" && (
<button
type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${
iAmFeelingLucky ? "cursor-wait" : ""
}`}
onClick={handleAutoGenerateDescription}
disabled={iAmFeelingLucky}
>
{iAmFeelingLucky ? (
"Generating response..."
) : (
<>
<Sparkle className="h-4 w-4" />I{"'"}m feeling lucky
</>
)}
</button>
)}
<button
type="button"
className="flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90"
onClick={() => setGptAssistantModal((prevData) => !prevData)}
>
<Sparkle className="h-4 w-4" />
AI
</button>
</div>
<Controller
name="description_html"
control={control}
@ -168,6 +259,23 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
/>
)}
/>
{envConfig?.has_openai_configured && (
<GptAssistantModal
isOpen={gptAssistantModal}
handleClose={() => {
setGptAssistantModal(false);
// this is done so that the title do not reset after gpt popover closed
reset(getValues());
}}
inset="top-2 left-0"
content=""
htmlContent={watch("description_html")}
onResponse={(response) => {
handleAiAssistance(response);
}}
projectId={projectId}
/>
)}
</div>
<div className="flex flex-wrap items-center gap-2">
@ -188,7 +296,7 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
onClick={() => setCreateMore((prevData) => !prevData)}
>
<span className="text-xs">Create more</span>
<ToggleSwitch value={createMore} onChange={() => { }} size="md" />
<ToggleSwitch value={createMore} onChange={() => {}} size="md" />
</div>
<div className="flex items-center gap-2">
<Button variant="neutral-primary" size="sm" onClick={() => handleClose()}>

View File

@ -1,6 +1,6 @@
import { FC, useCallback, useState } from "react";
import { DragDropContext, DropResult, Droppable } from "@hello-pangea/dnd";
import { useRouter } from "next/router";
import { DragDropContext, Droppable } from "@hello-pangea/dnd";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
@ -89,8 +89,12 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
projectLabel: { projectLabels },
projectMember: { projectMembers },
projectState: projectStateStore,
user: userStore,
} = useMobxStore();
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const issues = issueStore?.getIssues || {};
const issueIds = issueStore?.getIssuesIds || [];
@ -114,7 +118,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
setIsDragStarted(true);
};
const onDragEnd = (result: any) => {
const onDragEnd = (result: DropResult) => {
setIsDragStarted(false);
if (!result) return;
@ -159,7 +163,11 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragStart={onDragStart} onDragEnd={onDragEnd}>
<div className={`fixed left-1/2 -translate-x-1/2 z-40 w-72 top-3 flex items-center justify-center mx-3`}>
<div
className={`fixed left-1/2 -translate-x-1/2 ${
isDragStarted ? "z-40" : ""
} w-72 top-3 flex items-center justify-center mx-3`}
>
<Droppable droppableId="issue-trash-box" isDropDisabled={!isDragStarted}>
{(provided, snapshot) => (
<div
@ -216,7 +224,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
quickAddCallback={issueStore?.quickAddIssue}
viewId={viewId}
disableIssueCreation={!enableIssueCreation}
isReadOnly={!enableInlineEditing}
isReadOnly={!enableInlineEditing || !isEditingAllowed}
currentStore={currentStore}
addIssuesToView={addIssuesToView}
/>
@ -257,7 +265,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
isDragStarted={isDragStarted}
disableIssueCreation={true}
enableQuickIssueCreate={enableQuickAdd}
isReadOnly={!enableInlineEditing}
isReadOnly={!enableInlineEditing || !isEditingAllowed}
currentStore={currentStore}
addIssuesToView={(issues) => {
console.log("kanban existingIds", issues);

View File

@ -50,9 +50,13 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
});
};
let draggableId = issue.id;
if (columnId) draggableId = `${draggableId}__${columnId}`;
if (sub_group_id) draggableId = `${draggableId}__${sub_group_id}`;
return (
<>
<Draggable draggableId={issue.id} index={index}>
<Draggable draggableId={draggableId} index={index}>
{(provided, snapshot) => (
<div
className="group/kanban-block relative p-1.5 hover:cursor-default"

View File

@ -11,6 +11,7 @@ import { EIssueActions } from "../../types";
// components
import { BaseKanBanRoot } from "../base-kanban-root";
import { EProjectStore } from "store/command-palette.store";
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
export interface ICycleKanBanLayout {}
@ -51,8 +52,8 @@ export const CycleKanBanLayout: React.FC = observer(() => {
destination: any,
subGroupBy: string | null,
groupBy: string | null,
issues: IIssue[],
issueWithIds: any
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
) => {
if (kanBanHelperStore.handleDragDrop)
kanBanHelperStore.handleDragDrop(

View File

@ -11,6 +11,7 @@ import { IIssue } from "types";
import { EIssueActions } from "../../types";
import { BaseKanBanRoot } from "../base-kanban-root";
import { EProjectStore } from "store/command-palette.store";
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
export interface IModuleKanBanLayout {}
@ -30,28 +31,6 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
kanBanHelpers: kanBanHelperStore,
} = useMobxStore();
// const handleIssues = useCallback(
// (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => {
// if (!workspaceSlug || !moduleId) return;
// if (action === "update") {
// moduleIssueStore.updateIssueStructure(group_by, sub_group_by, issue);
// issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
// }
// if (action === "delete") moduleIssueStore.deleteIssue(group_by, sub_group_by, issue);
// if (action === "remove" && issue.bridge_id) {
// moduleIssueStore.deleteIssue(group_by, null, issue);
// moduleIssueStore.removeIssueFromModule(
// workspaceSlug.toString(),
// issue.project,
// moduleId.toString(),
// issue.bridge_id
// );
// }
// },
// [moduleIssueStore, issueDetailStore, moduleId, workspaceSlug]
// );
const issueActions = {
[EIssueActions.UPDATE]: async (issue: IIssue) => {
if (!workspaceSlug || !moduleId) return;
@ -73,8 +52,8 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
destination: any,
subGroupBy: string | null,
groupBy: string | null,
issues: IIssue[],
issueWithIds: any
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
) => {
if (kanBanHelperStore.handleDragDrop)
kanBanHelperStore.handleDragDrop(

View File

@ -30,7 +30,7 @@ export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
[EIssueActions.DELETE]: async (issue: IIssue) => {
if (!workspaceSlug || !userId) return;
await profileIssuesStore.removeIssue(workspaceSlug, userId, issue.project, issue.id);
await profileIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id, userId);
},
};

View File

@ -10,6 +10,7 @@ import { IIssue } from "types";
import { EIssueActions } from "../../types";
import { BaseKanBanRoot } from "../base-kanban-root";
import { EProjectStore } from "store/command-palette.store";
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
export interface IKanBanLayout {}
@ -42,8 +43,8 @@ export const KanBanLayout: React.FC = observer(() => {
destination: any,
subGroupBy: string | null,
groupBy: string | null,
issues: IIssue[],
issueWithIds: any
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
) => {
if (kanBanHelperStore.handleDragDrop)
kanBanHelperStore.handleDragDrop(

View File

@ -10,6 +10,7 @@ import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
// components
import { BaseKanBanRoot } from "../base-kanban-root";
import { EProjectStore } from "store/command-palette.store";
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
export interface IViewKanBanLayout {}
@ -42,8 +43,8 @@ export const ProjectViewKanBanLayout: React.FC = observer(() => {
destination: any,
subGroupBy: string | null,
groupBy: string | null,
issues: IIssue[],
issueWithIds: any
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
) => {
if (kanBanHelperStore.handleDragDrop)
kanBanHelperStore.handleDragDrop(

View File

@ -79,8 +79,12 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
projectMember: { projectMembers },
projectState: projectStateStore,
projectLabel: { projectLabels },
user: userStore,
} = useMobxStore();
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const issueIds = issueStore?.getIssuesIds || [];
const issues = issueStore?.getIssues;
@ -142,7 +146,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
viewId={viewId}
quickAddCallback={issueStore?.quickAddIssue}
enableIssueQuickAdd={!!enableQuickAdd}
isReadonly={!enableInlineEditing}
isReadonly={!enableInlineEditing || !isEditingAllowed}
disableIssueCreation={!enableIssueCreation}
currentStore={currentStore}
addIssuesToView={addIssuesToView}

View File

@ -30,7 +30,7 @@ export const ProfileIssuesListLayout: FC = observer(() => {
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
if (!workspaceSlug || !userId) return;
await profileIssuesStore.removeIssue(workspaceSlug, userId, issue.project, issue.id);
await profileIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id, userId);
},
};

View File

@ -68,7 +68,7 @@ export const CycleLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{Object.keys(getIssues ?? {}).length == 0 ? (
{Object.keys(getIssues ?? {}).length == 0 && !loader ? (
<CycleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} cycleId={cycleId} />
) : (
<div className="h-full w-full overflow-auto">

View File

@ -53,7 +53,7 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{Object.keys(getIssues ?? {}).length == 0 ? (
{Object.keys(getIssues ?? {}).length == 0 && !loader ? (
<ModuleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} moduleId={moduleId} />
) : (
<div className="h-full w-full overflow-auto">

View File

@ -53,7 +53,7 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{Object.keys(getIssues ?? {}).length == 0 ? (
{Object.keys(getIssues ?? {}).length == 0 && !loader ? (
<ProjectEmptyState />
) : (
<div className="w-full h-full relative overflow-auto">

View File

@ -48,7 +48,8 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
user: userStore,
} = useMobxStore();
const user = userStore.currentUser;
const { currentProjectRole } = userStore;
const isEditingAllowed = [15, 20].includes(currentProjectRole || 0);
const issuesResponse = issueStore.getIssues;
const issueIds = (issueStore.getIssuesIds ?? []) as TUnGroupedIssues;
@ -103,7 +104,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
labels={projectLabels || undefined}
states={projectId ? projectStateStore.states?.[projectId.toString()] : undefined}
handleIssues={handleIssues}
disableUserActions={false}
disableUserActions={!isEditingAllowed}
quickAddCallback={issueStore.quickAddIssue}
viewId={viewId}
enableQuickCreateIssue

View File

@ -132,7 +132,10 @@ export class InboxFiltersStore implements IInboxFiltersStore {
};
});
await this.inboxService.patchInbox(workspaceSlug, projectId, inboxId, { view_props: newViewProps });
const userRole = this.rootStore.user?.projectMemberInfo?.[projectId]?.role || 0;
if (userRole > 10) {
await this.inboxService.patchInbox(workspaceSlug, projectId, inboxId, { view_props: newViewProps });
}
} catch (error) {
runInAction(() => {
this.error = error;

View File

@ -1,15 +1,30 @@
import { DraggableLocation } from "@hello-pangea/dnd";
import { IProjectIssuesStore } from "./project-issues/project/issue.store";
import { IModuleIssuesStore } from "./project-issues/module/issue.store";
import { ICycleIssuesStore } from "./project-issues/cycle/issue.store";
import { IViewIssuesStore } from "./project-issues/project-view/issue.store";
import { IProjectDraftIssuesStore } from "./project-issues/draft/issue.store";
import { IProfileIssuesStore } from "./profile/issue.store";
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "./types";
export interface IKanBanHelpers {
// actions
handleDragDrop: (
source: any,
destination: any,
source: DraggableLocation | null,
destination: DraggableLocation | null,
workspaceSlug: string,
projectId: string,
store: any,
projectId: string, // projectId for all views or user id in profile issues
store:
| IProjectIssuesStore
| IModuleIssuesStore
| ICycleIssuesStore
| IViewIssuesStore
| IProjectDraftIssuesStore
| IProfileIssuesStore,
subGroupBy: string | null,
groupBy: string | null,
issues: any,
issueWithIds: any,
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined,
viewId?: string | null
) => void;
}
@ -53,108 +68,125 @@ export class KanBanHelpers implements IKanBanHelpers {
};
handleDragDrop = async (
source: any,
destination: any,
source: DraggableLocation | null,
destination: DraggableLocation | null,
workspaceSlug: string,
projectId: string, // projectId for all views or user id in profile issues
store: any,
store:
| IProjectIssuesStore
| IModuleIssuesStore
| ICycleIssuesStore
| IViewIssuesStore
| IProjectDraftIssuesStore
| IProfileIssuesStore,
subGroupBy: string | null,
groupBy: string | null,
issues: any,
issueWithIds: any,
issues: IIssueResponse | undefined,
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined,
viewId: string | null = null // it can be moduleId, cycleId
) => {
if (issues && issueWithIds) {
let updateIssue: any = {};
if (!issues || !issueWithIds || !source || !destination) return;
const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null;
const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null;
let updateIssue: any = {};
const sourceGroupByColumnId = sourceColumnId[0] || null;
const destinationGroupByColumnId = destinationColumnId[0] || null;
const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null;
const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null;
const sourceSubGroupByColumnId = sourceColumnId[1] || null;
const destinationSubGroupByColumnId = destinationColumnId[1] || null;
if (!sourceColumnId || !destinationColumnId) return;
if (!workspaceSlug || !projectId || !groupBy || !sourceGroupByColumnId || !destinationGroupByColumnId) return;
const sourceGroupByColumnId = sourceColumnId[0] || null;
const destinationGroupByColumnId = destinationColumnId[0] || null;
if (destinationGroupByColumnId === "issue-trash-box") {
const sourceIssues = subGroupBy
? issueWithIds[sourceSubGroupByColumnId][sourceGroupByColumnId]
: issueWithIds[sourceGroupByColumnId];
const sourceSubGroupByColumnId = sourceColumnId[1] || null;
const destinationSubGroupByColumnId = destinationColumnId[1] || null;
const [removed] = sourceIssues.splice(source.index, 1);
if (
!workspaceSlug ||
!projectId ||
!groupBy ||
!sourceGroupByColumnId ||
!destinationGroupByColumnId ||
!sourceSubGroupByColumnId ||
!sourceGroupByColumnId
)
return;
console.log("removed", removed);
if (destinationGroupByColumnId === "issue-trash-box") {
const sourceIssues: string[] = subGroupBy
? (issueWithIds as ISubGroupedIssues)[sourceSubGroupByColumnId][sourceGroupByColumnId]
: (issueWithIds as IGroupedIssues)[sourceGroupByColumnId];
if (removed) {
if (viewId) store?.removeIssue(workspaceSlug, projectId, removed, viewId);
else store?.removeIssue(workspaceSlug, projectId, removed);
}
} else {
const sourceIssues = subGroupBy
? issueWithIds[sourceSubGroupByColumnId][sourceGroupByColumnId]
: issueWithIds[sourceGroupByColumnId];
const destinationIssues = subGroupBy
? issueWithIds[sourceSubGroupByColumnId][destinationGroupByColumnId]
: issueWithIds[destinationGroupByColumnId];
const [removed] = sourceIssues.splice(source.index, 1);
const [removed] = sourceIssues.splice(source.index, 1);
const removedIssueDetail = issues[removed];
console.log("removed", removed);
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
updateIssue = {
id: removedIssueDetail?.id,
};
if (removed) {
if (viewId) store?.removeIssue(workspaceSlug, projectId, removed, viewId);
else store?.removeIssue(workspaceSlug, projectId, removed);
}
} else {
const sourceIssues = subGroupBy
? (issueWithIds as ISubGroupedIssues)[sourceSubGroupByColumnId][sourceGroupByColumnId]
: (issueWithIds as IGroupedIssues)[sourceGroupByColumnId];
const destinationIssues = subGroupBy
? (issueWithIds as ISubGroupedIssues)[sourceSubGroupByColumnId][destinationGroupByColumnId]
: (issueWithIds as IGroupedIssues)[destinationGroupByColumnId];
// for both horizontal and vertical dnd
updateIssue = {
...updateIssue,
...this.handleSortOrder(destinationIssues, destination.index, issues),
};
const [removed] = sourceIssues.splice(source.index, 1);
const removedIssueDetail = issues[removed];
if (sourceSubGroupByColumnId === destinationSubGroupByColumnId) {
if (sourceGroupByColumnId != destinationGroupByColumnId) {
if (groupBy === "state") updateIssue = { ...updateIssue, state: destinationGroupByColumnId };
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
}
} else {
if (subGroupBy === "state")
updateIssue = {
...updateIssue,
state: destinationSubGroupByColumnId,
priority: destinationGroupByColumnId,
};
if (subGroupBy === "priority")
updateIssue = {
...updateIssue,
state: destinationGroupByColumnId,
priority: destinationSubGroupByColumnId,
};
}
} else {
updateIssue = {
id: removedIssueDetail?.id,
};
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
updateIssue = {
id: removedIssueDetail?.id,
};
// for both horizontal and vertical dnd
updateIssue = {
...updateIssue,
...this.handleSortOrder(destinationIssues, destination.index, issues),
};
// for both horizontal and vertical dnd
updateIssue = {
...updateIssue,
...this.handleSortOrder(destinationIssues, destination.index, issues),
};
// for horizontal dnd
if (sourceColumnId != destinationColumnId) {
if (sourceSubGroupByColumnId === destinationSubGroupByColumnId) {
if (sourceGroupByColumnId != destinationGroupByColumnId) {
if (groupBy === "state") updateIssue = { ...updateIssue, state: destinationGroupByColumnId };
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
}
} else {
if (subGroupBy === "state")
updateIssue = {
...updateIssue,
state: destinationSubGroupByColumnId,
priority: destinationGroupByColumnId,
};
if (subGroupBy === "priority")
updateIssue = {
...updateIssue,
state: destinationGroupByColumnId,
priority: destinationSubGroupByColumnId,
};
}
} else {
updateIssue = {
id: removedIssueDetail?.id,
};
if (updateIssue && updateIssue?.id) {
if (viewId) store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue, viewId);
else store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
// for both horizontal and vertical dnd
updateIssue = {
...updateIssue,
...this.handleSortOrder(destinationIssues, destination.index, issues),
};
// for horizontal dnd
if (sourceColumnId != destinationColumnId) {
if (groupBy === "state") updateIssue = { ...updateIssue, state: destinationGroupByColumnId };
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
}
}
if (updateIssue && updateIssue?.id) {
if (viewId) store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue, viewId);
else store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
}
}
};
}

View File

@ -40,9 +40,9 @@ export interface IProfileIssuesStore {
) => Promise<IIssue | undefined>;
removeIssue: (
workspaceSlug: string,
userId: string,
projectId: string,
issueId: string
issueId: string,
userId?: string
) => Promise<IIssue | undefined>;
quickAddIssue: (workspaceSlug: string, userId: string, data: IIssue) => Promise<IIssue | undefined>;
viewFlags: ViewFlags;
@ -275,7 +275,8 @@ export class ProfileIssuesStore extends IssueBaseStore implements IProfileIssues
}
};
removeIssue = async (workspaceSlug: string, userId: string, projectId: string, issueId: string) => {
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, userId?: string) => {
if (!userId) return;
try {
let _issues = { ...this.issues };
if (!_issues) _issues = {};