fix: In kanban issues can be shifted between the column in order_by (#2676)

This commit is contained in:
guru_sainath 2023-11-06 21:00:36 +05:30 committed by GitHub
parent 46f307fed5
commit 984b36f45a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 450 additions and 276 deletions

View File

@ -42,7 +42,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
return (
<>
<Draggable draggableId={issue.id} index={index} isDragDisabled={isDragDisabled}>
<Draggable draggableId={issue.id} index={index}>
{(provided, snapshot) => (
<div
className="group/kanban-block relative p-1.5 hover:cursor-default"

View File

@ -10,11 +10,13 @@ import { KanbanIssueBlocksList, BoardInlineCreateIssueForm } from "components/is
import { IIssueDisplayProperties, IIssue } from "types";
// constants
import { getValueFromObject } from "constants/issue";
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
export interface IGroupByKanBan {
issues: any;
sub_group_by: string | null;
group_by: string | null;
order_by: string | null;
sub_group_id: string;
list: any;
listKey: string;
@ -31,6 +33,7 @@ export interface IGroupByKanBan {
kanBanToggle: any;
handleKanBanToggle: any;
enableQuickIssueCreate?: boolean;
isDragStarted?: boolean;
}
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
@ -38,6 +41,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
issues,
sub_group_by,
group_by,
order_by,
sub_group_id = "null",
list,
listKey,
@ -49,6 +53,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
kanBanToggle,
handleKanBanToggle,
enableQuickIssueCreate,
isDragStarted,
} = props;
const verticalAlignPosition = (_list: any) =>
@ -59,7 +64,9 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
{list &&
list.length > 0 &&
list.map((_list: any) => (
<div className={`flex-shrink-0 flex flex-col ${!verticalAlignPosition(_list) ? `w-[340px]` : ``}`}>
<div
className={`relative flex-shrink-0 flex flex-col ${!verticalAlignPosition(_list) ? `w-[340px]` : ``} group`}
>
{sub_group_by === null && (
<div className="flex-shrink-0 w-full bg-custom-background-90 py-1 sticky top-0 z-[2]">
<KanBanGroupByHeaderRoot
@ -79,7 +86,10 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
verticalAlignPosition(_list) ? `w-[0px] overflow-hidden` : `w-full transition-all`
}`}
>
<Droppable droppableId={`${getValueFromObject(_list, listKey) as string}__${sub_group_id}`}>
<Droppable
droppableId={`${getValueFromObject(_list, listKey) as string}__${sub_group_id}`}
isDropDisabled={isDragDisabled}
>
{(provided: any, snapshot: any) => (
<div
className={`w-full h-full relative transition-all ${
@ -101,25 +111,39 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
/>
) : (
isDragDisabled && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center text-sm">
{/* <div className="text-custom-text-300 text-sm">Drop here</div> */}
</div>
)
)}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
{enableQuickIssueCreate && (
<BoardInlineCreateIssueForm
groupId={getValueFromObject(_list, listKey) as string}
subGroupId={sub_group_id}
prePopulatedData={{
...(group_by && { [group_by]: getValueFromObject(_list, listKey) }),
...(sub_group_by && sub_group_id !== "null" && { [sub_group_by]: sub_group_id }),
}}
/>
<div className="flex-shrink-0 w-full bg-custom-background-90 py-1 sticky bottom-0 z-[0]">
{enableQuickIssueCreate && (
<BoardInlineCreateIssueForm
groupId={getValueFromObject(_list, listKey) as string}
subGroupId={sub_group_id}
prePopulatedData={{
...(group_by && { [group_by]: getValueFromObject(_list, listKey) }),
...(sub_group_by && sub_group_id !== "null" && { [sub_group_by]: sub_group_id }),
}}
/>
)}
</div>
{isDragStarted && isDragDisabled && (
<div className="invisible group-hover:visible transition-all text-sm absolute top-12 bottom-10 left-0 right-0 bg-custom-background-100/40 text-center">
<div className="rounded inline-flex mt-80 h-8 px-3 justify-center items-center bg-custom-background-80 text-custom-text-100 font-medium">
{`This board is ordered by "${replaceUnderscoreIfSnakeCase(
order_by ? (order_by[0] === "-" ? order_by.slice(1) : order_by) : "created_at"
)}"`}
</div>
</div>
)}
</div>
))}
@ -131,8 +155,8 @@ export interface IKanBan {
issues: any;
sub_group_by: string | null;
group_by: string | null;
order_by: string | null;
sub_group_id?: string;
handleDragDrop?: (result: any) => void | undefined;
handleIssues: (
sub_group_by: string | null,
group_by: string | null,
@ -151,6 +175,7 @@ export interface IKanBan {
members: any;
projects: any;
enableQuickIssueCreate?: boolean;
isDragStarted?: boolean;
}
export const KanBan: React.FC<IKanBan> = observer((props) => {
@ -158,6 +183,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
issues,
sub_group_by,
group_by,
order_by,
sub_group_id = "null",
handleIssues,
quickActions,
@ -172,6 +198,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
members,
projects,
enableQuickIssueCreate,
isDragStarted,
} = props;
const { issueKanBanView: issueKanBanViewStore } = useMobxStore();
@ -182,6 +209,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={projects}
@ -194,6 +222,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -201,6 +230,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={states}
@ -213,6 +243,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -220,6 +251,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={stateGroups}
@ -232,6 +264,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -239,6 +272,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={priorities}
@ -251,6 +285,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -258,6 +293,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={labels ? [...labels, { id: "None", name: "None" }] : labels}
@ -270,6 +306,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -277,6 +314,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={members ? [...members, { id: "None", display_name: "None" }] : members}
@ -289,6 +327,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
@ -296,6 +335,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
<GroupByKanBan
issues={issues}
group_by={group_by}
order_by={order_by}
sub_group_by={sub_group_by}
sub_group_id={sub_group_id}
list={members}
@ -308,6 +348,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
kanBanToggle={kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
enableQuickIssueCreate={enableQuickIssueCreate}
isDragStarted={isDragStarted}
/>
)}
</div>

View File

@ -1,4 +1,4 @@
import React, { useCallback } from "react";
import React, { useCallback, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { DragDropContext } from "@hello-pangea/dnd";
@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { KanBanSwimLanes } from "../swimlanes";
import { KanBan } from "../default";
import { CycleIssueQuickActions } from "components/issues";
import { Spinner } from "@plane/ui";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// types
@ -37,6 +38,8 @@ export const CycleKanBanLayout: React.FC = observer(() => {
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null;
const userDisplayFilters = issueFilterStore?.userDisplayFilters || null;
const displayProperties = issueFilterStore?.userDisplayProperties || null;
@ -45,7 +48,15 @@ export const CycleKanBanLayout: React.FC = observer(() => {
? "swimlanes"
: "default";
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
const onDragStart = () => {
setIsDragStarted(true);
};
const onDragEnd = (result: any) => {
setIsDragStarted(false);
if (!result) return;
if (
@ -99,60 +110,72 @@ export const CycleKanBanLayout: React.FC = observer(() => {
: null;
return (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<CycleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
<>
{cycleIssueStore.loader ? (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
) : (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<CycleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<CycleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
)}
displayProperties={displayProperties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<CycleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromCycle={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
)}
</DragDropContext>
</div>
</DragDropContext>
</div>
)}
</>
);
});

View File

@ -1,4 +1,4 @@
import React, { useCallback } from "react";
import React, { useCallback, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { DragDropContext } from "@hello-pangea/dnd";
@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { KanBanSwimLanes } from "../swimlanes";
import { KanBan } from "../default";
import { ModuleIssueQuickActions } from "components/issues";
import { Spinner } from "@plane/ui";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// types
@ -36,6 +37,8 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null;
const userDisplayFilters = issueFilterStore?.userDisplayFilters || null;
const displayProperties = issueFilterStore?.userDisplayProperties || null;
@ -44,7 +47,14 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
? "swimlanes"
: "default";
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
const onDragStart = () => {
setIsDragStarted(true);
};
const onDragEnd = (result: any) => {
setIsDragStarted(false);
if (!result) return;
if (
@ -98,60 +108,72 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
: null;
return (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ModuleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
<>
{moduleIssueStore.loader ? (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
) : (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ModuleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ModuleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
)}
displayProperties={displayProperties}
kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ModuleIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
handleRemoveFromModule={async () => handleIssues(sub_group_by, group_by, issue, "remove")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={moduleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
)}
</DragDropContext>
</div>
</DragDropContext>
</div>
)}
</>
);
});

View File

@ -1,4 +1,4 @@
import { FC, useCallback } from "react";
import { FC, useCallback, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { DragDropContext } from "@hello-pangea/dnd";
@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { KanBanSwimLanes } from "../swimlanes";
import { KanBan } from "../default";
import { ProjectIssueQuickActions } from "components/issues";
import { Spinner } from "@plane/ui";
// constants
import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
// types
@ -34,6 +35,8 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => {
const group_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.group_by || null;
const order_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.order_by || null;
const userDisplayFilters = profileIssueFiltersStore?.userDisplayFilters || null;
const displayProperties = profileIssueFiltersStore?.userDisplayProperties || null;
@ -42,7 +45,14 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => {
? "swimlanes"
: "default";
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
const onDragStart = () => {
setIsDragStarted(true);
};
const onDragEnd = (result: any) => {
setIsDragStarted(false);
if (!result) return;
if (
@ -83,58 +93,70 @@ export const ProfileIssuesKanBanLayout: FC = observer(() => {
const projects = projectStore?.workspaceProjects || null;
return (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
<>
{profileIssuesStore.loader ? (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
) : (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
)}
</DragDropContext>
</div>
</DragDropContext>
</div>
)}
</>
);
});

View File

@ -1,4 +1,4 @@
import { useCallback } from "react";
import { useCallback, useState } from "react";
import { useRouter } from "next/router";
import { DragDropContext } from "@hello-pangea/dnd";
import { observer } from "mobx-react-lite";
@ -8,6 +8,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { KanBanSwimLanes } from "../swimlanes";
import { KanBan } from "../default";
import { ProjectIssueQuickActions } from "components/issues";
import { Spinner } from "@plane/ui";
// types
import { IIssue } from "types";
// constants
@ -34,6 +35,8 @@ export const KanBanLayout: React.FC = observer(() => {
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
const order_by: string | null = issueFilterStore?.userDisplayFilters?.order_by || null;
const userDisplayFilters = issueFilterStore?.userDisplayFilters || null;
const displayProperties = issueFilterStore?.userDisplayProperties || null;
@ -42,12 +45,22 @@ export const KanBanLayout: React.FC = observer(() => {
? "swimlanes"
: "default";
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
const onDragStart = () => {
setIsDragStarted(true);
};
const onDragEnd = (result: any) => {
setIsDragStarted(false);
if (!result) return;
if (
result.destination &&
result.source &&
result.source.droppableId &&
result.destination.droppableId &&
result.destination.droppableId === result.source.droppableId &&
result.destination.index === result.source.index
)
@ -87,59 +100,71 @@ export const KanBanLayout: React.FC = observer(() => {
: null;
return (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
<>
{issueStore.loader ? (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
) : (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragStart={onDragStart} onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
enableQuickIssueCreate
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
isDragStarted={isDragStarted}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
enableQuickIssueCreate
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(sub_group_by, group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
/>
)}
</DragDropContext>
</div>
</DragDropContext>
</div>
)}
</>
);
});

View File

@ -58,6 +58,7 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
};
interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
order_by: string | null;
showEmptyGroup: boolean;
states: IState[] | null;
stateGroups: any;
@ -76,12 +77,14 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
displayProperties: IIssueDisplayProperties;
kanBanToggle: any;
handleKanBanToggle: any;
isDragStarted?: boolean;
}
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
const {
issues,
sub_group_by,
group_by,
order_by,
list,
listKey,
handleIssues,
@ -96,6 +99,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
labels,
members,
projects,
isDragStarted,
} = props;
const calculateIssueCount = (column_id: string) => {
@ -133,6 +137,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
issues={issues?.[getValueFromObject(_list, listKey) as string]}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
sub_group_id={getValueFromObject(_list, listKey) as string}
handleIssues={handleIssues}
quickActions={quickActions}
@ -147,6 +152,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
members={members}
projects={projects}
enableQuickIssueCreate
isDragStarted={isDragStarted}
/>
</div>
)}
@ -160,6 +166,7 @@ export interface IKanBanSwimLanes {
issues: any;
sub_group_by: string | null;
group_by: string | null;
order_by: string | null;
handleIssues: (
sub_group_by: string | null,
group_by: string | null,
@ -177,6 +184,7 @@ export interface IKanBanSwimLanes {
labels: IIssueLabels[] | null;
members: IUserLite[] | null;
projects: IProject[] | null;
isDragStarted?: boolean;
}
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
@ -184,6 +192,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues,
sub_group_by,
group_by,
order_by,
handleIssues,
quickActions,
displayProperties,
@ -196,6 +205,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels,
members,
projects,
isDragStarted,
} = props;
return (
@ -291,6 +301,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={projects}
listKey={`id`}
handleIssues={handleIssues}
@ -305,6 +316,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -313,6 +325,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={states}
listKey={`id`}
handleIssues={handleIssues}
@ -327,6 +340,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -335,6 +349,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={states}
listKey={`id`}
handleIssues={handleIssues}
@ -349,6 +364,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -357,6 +373,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={stateGroups}
listKey={`key`}
handleIssues={handleIssues}
@ -371,6 +388,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -379,6 +397,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={priorities}
listKey={`key`}
handleIssues={handleIssues}
@ -393,6 +412,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -401,6 +421,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={labels ? [...labels, { id: "None", name: "None" }] : labels}
listKey={`id`}
handleIssues={handleIssues}
@ -415,6 +436,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -423,6 +445,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={members ? [...members, { id: "None", display_name: "None" }] : members}
listKey={`id`}
handleIssues={handleIssues}
@ -437,6 +460,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
@ -445,6 +469,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
order_by={order_by}
list={members}
listKey={`id`}
handleIssues={handleIssues}
@ -459,6 +484,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
labels={labels}
members={members}
projects={projects}
isDragStarted={isDragStarted}
/>
)}
</div>

View File

@ -6,6 +6,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components
import { List } from "../default";
import { ProjectIssueQuickActions } from "components/issues";
import { Spinner } from "@plane/ui";
// helpers
import { orderArrayBy } from "helpers/array.helper";
// types
@ -56,29 +57,37 @@ export const ListLayout: FC = observer(() => {
: null;
return (
<div className="relative w-full h-full bg-custom-background-90">
<List
issues={issues}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(group_by, data, "update")}
<>
{issueStore.loader ? (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
) : (
<div className="relative w-full h-full bg-custom-background-90">
<List
issues={issues}
group_by={group_by}
handleIssues={handleIssues}
quickActions={(group_by, issue) => (
<ProjectIssueQuickActions
issue={issue}
handleDelete={async () => handleIssues(group_by, issue, "delete")}
handleUpdate={async (data) => handleIssues(group_by, data, "update")}
/>
)}
displayProperties={displayProperties}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
enableQuickIssueCreate
estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null}
showEmptyGroup={userDisplayFilters.show_empty_groups}
/>
)}
displayProperties={displayProperties}
states={states}
stateGroups={stateGroups}
priorities={priorities}
labels={labels}
members={members?.map((m) => m.member) ?? null}
projects={projects}
enableQuickIssueCreate
estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null}
showEmptyGroup={userDisplayFilters.show_empty_groups}
/>
</div>
</div>
)}
</>
);
});

View File

@ -22,13 +22,19 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
if (workspaceSlug && projectId) {
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
const { isLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null,
async () => {
if (workspaceSlug && projectId) {
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
}
}
});
);
console.log("--");
console.log("isLoading -- -->", isLoading);
console.log("--");
const activeLayout = issueFilterStore.userDisplayFilters.layout;

View File

@ -36,8 +36,8 @@ export class CycleIssueCalendarViewStore implements ICycleIssueCalendarViewStore
projectId: projectId,
};
const droppableSourceColumnId = source.droppableId;
const droppableDestinationColumnId = destination.droppableId;
const droppableSourceColumnId = source?.droppableId || null;
const droppableDestinationColumnId = destination?.droppableId || null;
if (droppableSourceColumnId === droppableDestinationColumnId) return;

View File

@ -95,9 +95,9 @@ export class CycleIssueKanBanViewStore implements ICycleIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;
@ -315,9 +315,9 @@ export class CycleIssueKanBanViewStore implements ICycleIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;

View File

@ -35,8 +35,8 @@ export class IssueCalendarViewStore implements IIssueCalendarViewStore {
projectId: projectId,
};
const droppableSourceColumnId = source.droppableId;
const droppableDestinationColumnId = destination.droppableId;
const droppableSourceColumnId = source?.droppableId || null;
const droppableDestinationColumnId = destination?.droppableId || null;
if (droppableSourceColumnId === droppableDestinationColumnId) return;

View File

@ -95,9 +95,9 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;
@ -315,9 +315,9 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;

View File

@ -36,8 +36,8 @@ export class ModuleIssueCalendarViewStore implements IModuleIssueCalendarViewSto
projectId: projectId,
};
const droppableSourceColumnId = source.droppableId;
const droppableDestinationColumnId = destination.droppableId;
const droppableSourceColumnId = source?.droppableId || null;
const droppableDestinationColumnId = destination?.droppableId || null;
if (droppableSourceColumnId === droppableDestinationColumnId) return;

View File

@ -95,9 +95,9 @@ export class ModuleIssueKanBanViewStore implements IModuleIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;
@ -315,9 +315,9 @@ export class ModuleIssueKanBanViewStore implements IModuleIssueKanBanViewStore {
};
// source, destination group and sub group id
let droppableSourceColumnId = source.droppableId;
let droppableSourceColumnId = source?.droppableId || null;
droppableSourceColumnId = droppableSourceColumnId ? droppableSourceColumnId.split("__") : null;
let droppableDestinationColumnId = destination.droppableId;
let droppableDestinationColumnId = destination?.droppableId || null;
droppableDestinationColumnId = droppableDestinationColumnId ? droppableDestinationColumnId.split("__") : null;
if (!droppableSourceColumnId || !droppableDestinationColumnId) return null;

View File

@ -36,8 +36,8 @@ export class ProjectViewIssueCalendarViewStore implements IProjectViewIssueCalen
projectId: projectId,
};
const droppableSourceColumnId = source.droppableId;
const droppableDestinationColumnId = destination.droppableId;
const droppableSourceColumnId = source?.droppableId || null;
const droppableDestinationColumnId = destination?.droppableId || null;
if (droppableSourceColumnId === droppableDestinationColumnId) return;