mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Kanban new store implementation for project issues
This commit is contained in:
parent
78bc9f615f
commit
fd1f262bf7
@ -6,17 +6,21 @@ import { Button } from "@plane/ui";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// types
|
// types
|
||||||
import type { IIssue } from "types";
|
import { useIssues } from "hooks/store/use-issues";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
data: IIssue;
|
dataId: string;
|
||||||
onSubmit?: () => Promise<void>;
|
onSubmit?: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteIssueModal: React.FC<Props> = (props) => {
|
export const DeleteIssueModal: React.FC<Props> = (props) => {
|
||||||
const { data, isOpen, handleClose, onSubmit } = props;
|
const { dataId, isOpen, handleClose, onSubmit } = props;
|
||||||
|
|
||||||
|
const { issueMap } = useIssues();
|
||||||
|
|
||||||
|
const issue = issueMap.allIssues[dataId];
|
||||||
|
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
@ -93,7 +97,7 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
|||||||
<p className="text-sm text-custom-text-200">
|
<p className="text-sm text-custom-text-200">
|
||||||
Are you sure you want to delete issue{" "}
|
Are you sure you want to delete issue{" "}
|
||||||
<span className="break-words font-medium text-custom-text-100">
|
<span className="break-words font-medium text-custom-text-100">
|
||||||
{data?.project_detail?.identifier}-{data?.sequence_id}
|
{issue?.project_detail?.identifier}-{issue?.sequence_id}
|
||||||
</span>
|
</span>
|
||||||
{""}? All of the data related to the issue will be permanently removed. This action cannot be
|
{""}? All of the data related to the issue will be permanently removed. This action cannot be
|
||||||
undone.
|
undone.
|
||||||
|
@ -12,35 +12,26 @@ import { IIssue } from "types";
|
|||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import {
|
import {
|
||||||
ICycleIssuesFilterStore,
|
ICycleIssuesFilterStore,
|
||||||
ICycleIssuesStore,
|
|
||||||
IModuleIssuesFilterStore,
|
IModuleIssuesFilterStore,
|
||||||
IModuleIssuesStore,
|
|
||||||
IProfileIssuesFilterStore,
|
IProfileIssuesFilterStore,
|
||||||
IProfileIssuesStore,
|
|
||||||
IProjectDraftIssuesStore,
|
|
||||||
IProjectIssuesFilterStore,
|
IProjectIssuesFilterStore,
|
||||||
IProjectIssuesStore,
|
|
||||||
IViewIssuesFilterStore,
|
IViewIssuesFilterStore,
|
||||||
IViewIssuesStore,
|
|
||||||
} from "store_legacy/issues";
|
} from "store_legacy/issues";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
import { IIssueKanBanViewStore } from "store_legacy/issue";
|
import { IProjectIssues } from "store/issue/project";
|
||||||
//components
|
//components
|
||||||
import { KanBan } from "./default";
|
import { KanBan } from "./default";
|
||||||
import { KanBanSwimLanes } from "./swimlanes";
|
import { KanBanSwimLanes } from "./swimlanes";
|
||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
import { DeleteIssueModal, IssuePeekOverview } from "components/issues";
|
import { DeleteIssueModal, IssuePeekOverview } from "components/issues";
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
|
import { useIssues } from "hooks/store/use-issues";
|
||||||
|
import { handleDragDrop } from "./utils";
|
||||||
|
import { IIssueKanBanViewStore } from "store/issue/issue_kanban_view.store";
|
||||||
|
|
||||||
export interface IBaseKanBanLayout {
|
export interface IBaseKanBanLayout {
|
||||||
issueStore:
|
issues: IProjectIssues;
|
||||||
| IProjectIssuesStore
|
issuesFilter:
|
||||||
| IModuleIssuesStore
|
|
||||||
| ICycleIssuesStore
|
|
||||||
| IViewIssuesStore
|
|
||||||
| IProjectDraftIssuesStore
|
|
||||||
| IProfileIssuesStore;
|
|
||||||
issuesFilterStore:
|
|
||||||
| IProjectIssuesFilterStore
|
| IProjectIssuesFilterStore
|
||||||
| IModuleIssuesFilterStore
|
| IModuleIssuesFilterStore
|
||||||
| ICycleIssuesFilterStore
|
| ICycleIssuesFilterStore
|
||||||
@ -56,14 +47,6 @@ export interface IBaseKanBanLayout {
|
|||||||
showLoader?: boolean;
|
showLoader?: boolean;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
currentStore?: EProjectStore;
|
currentStore?: EProjectStore;
|
||||||
handleDragDrop?: (
|
|
||||||
source: any,
|
|
||||||
destination: any,
|
|
||||||
subGroupBy: string | null,
|
|
||||||
groupBy: string | null,
|
|
||||||
issues: any,
|
|
||||||
issueWithIds: any
|
|
||||||
) => Promise<IIssue | undefined>;
|
|
||||||
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
addIssuesToView?: (issueIds: string[]) => Promise<IIssue>;
|
||||||
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
||||||
}
|
}
|
||||||
@ -76,33 +59,31 @@ type KanbanDragState = {
|
|||||||
|
|
||||||
export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBaseKanBanLayout) => {
|
export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBaseKanBanLayout) => {
|
||||||
const {
|
const {
|
||||||
issueStore,
|
issues,
|
||||||
issuesFilterStore,
|
issuesFilter,
|
||||||
kanbanViewStore,
|
kanbanViewStore,
|
||||||
QuickActions,
|
QuickActions,
|
||||||
issueActions,
|
issueActions,
|
||||||
showLoader,
|
showLoader,
|
||||||
viewId,
|
viewId,
|
||||||
currentStore,
|
currentStore,
|
||||||
handleDragDrop,
|
|
||||||
addIssuesToView,
|
addIssuesToView,
|
||||||
canEditPropertiesBasedOnProject,
|
canEditPropertiesBasedOnProject,
|
||||||
} = props;
|
} = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, peekIssueId, peekProjectId } = router.query;
|
const { workspaceSlug, projectId, peekIssueId, peekProjectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { issueMap } = useIssues();
|
||||||
// toast alert
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const issues = issueStore?.getIssues || {};
|
const issueIds = issues?.getIssuesIds || [];
|
||||||
const issueIds = issueStore?.getIssuesIds || [];
|
|
||||||
|
|
||||||
const displayFilters = issuesFilterStore?.issueFilters?.displayFilters;
|
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
|
||||||
const displayProperties = issuesFilterStore?.issueFilters?.displayProperties || null;
|
|
||||||
|
|
||||||
const sub_group_by: string | null = displayFilters?.sub_group_by || null;
|
const sub_group_by: string | null = displayFilters?.sub_group_by || null;
|
||||||
const group_by: string | null = displayFilters?.group_by || null;
|
const group_by: string | null = displayFilters?.group_by || null;
|
||||||
@ -111,7 +92,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
|
|
||||||
const KanBanView = sub_group_by ? KanBanSwimLanes : KanBan;
|
const KanBanView = sub_group_by ? KanBanSwimLanes : KanBan;
|
||||||
|
|
||||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
||||||
@ -161,15 +142,23 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
});
|
});
|
||||||
setDeleteIssueModal(true);
|
setDeleteIssueModal(true);
|
||||||
} else {
|
} else {
|
||||||
await handleDragDrop(result.source, result.destination, sub_group_by, group_by, issues, issueIds).catch(
|
await handleDragDrop(
|
||||||
(err) => {
|
result.source,
|
||||||
setToastAlert({
|
result.destination,
|
||||||
title: "Error",
|
workspaceSlug.toString(),
|
||||||
type: "error",
|
projectId.toString(),
|
||||||
message: err.detail ?? "Failed to perform this action",
|
issues,
|
||||||
});
|
sub_group_by,
|
||||||
}
|
group_by,
|
||||||
);
|
issueMap,
|
||||||
|
issueIds
|
||||||
|
).catch((err) => {
|
||||||
|
setToastAlert({
|
||||||
|
title: "Error",
|
||||||
|
type: "error",
|
||||||
|
message: err.detail ?? "Failed to perform this action",
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -202,12 +191,20 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
|
|
||||||
const handleDeleteIssue = async () => {
|
const handleDeleteIssue = async () => {
|
||||||
if (!handleDragDrop) return;
|
if (!handleDragDrop) return;
|
||||||
await handleDragDrop(dragState.source, dragState.destination, sub_group_by, group_by, issues, issueIds).finally(
|
await handleDragDrop(
|
||||||
() => {
|
dragState.source,
|
||||||
setDeleteIssueModal(false);
|
dragState.destination,
|
||||||
setDragState({});
|
workspaceSlug.toString(),
|
||||||
}
|
projectId.toString(),
|
||||||
);
|
issues,
|
||||||
|
sub_group_by,
|
||||||
|
group_by,
|
||||||
|
issueMap,
|
||||||
|
issueIds
|
||||||
|
).finally(() => {
|
||||||
|
setDeleteIssueModal(false);
|
||||||
|
setDragState({});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {
|
const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {
|
||||||
@ -217,13 +214,13 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteIssueModal
|
<DeleteIssueModal
|
||||||
data={dragState.draggedIssueId ? issues[dragState.draggedIssueId] : ({} as IIssue)}
|
dataId={dragState.draggedIssueId}
|
||||||
isOpen={deleteIssueModal}
|
isOpen={deleteIssueModal}
|
||||||
handleClose={() => setDeleteIssueModal(false)}
|
handleClose={() => setDeleteIssueModal(false)}
|
||||||
onSubmit={handleDeleteIssue}
|
onSubmit={handleDeleteIssue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showLoader && issueStore?.loader === "init-loader" && (
|
{showLoader && issues?.loader === "init-loader" && (
|
||||||
<div className="fixed right-2 top-16 z-30 flex h-10 w-10 items-center justify-center rounded bg-custom-background-80 shadow-custom-shadow-sm">
|
<div className="fixed right-2 top-16 z-30 flex h-10 w-10 items-center justify-center rounded bg-custom-background-80 shadow-custom-shadow-sm">
|
||||||
<Spinner className="h-5 w-5" />
|
<Spinner className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
@ -254,18 +251,18 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<KanBanView
|
<KanBanView
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
sub_group_by={sub_group_by}
|
sub_group_by={sub_group_by}
|
||||||
group_by={group_by}
|
group_by={group_by}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={renderQuickActions}
|
quickActions={renderQuickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={kanbanViewStore?.kanBanToggle}
|
kanBanToggle={kanbanViewStore?.kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
enableQuickIssueCreate={enableQuickAdd}
|
enableQuickIssueCreate={enableQuickAdd}
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
||||||
quickAddCallback={issueStore?.quickAddIssue}
|
quickAddCallback={issues?.quickAddIssue}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
|
||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
|
@ -1,39 +1,57 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Draggable } from "@hello-pangea/dnd";
|
import { Draggable } from "@hello-pangea/dnd";
|
||||||
import isEqual from "lodash/isEqual";
|
|
||||||
// components
|
// components
|
||||||
import { KanBanProperties } from "./properties";
|
import { KanBanProperties } from "./properties";
|
||||||
// ui
|
// ui
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayProperties, IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
import { IssuesFilter } from "store/issue/base-issue-filter.store";
|
||||||
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
|
|
||||||
interface IssueBlockProps {
|
interface IssueBlockProps {
|
||||||
sub_group_id: string;
|
sub_group_id: string;
|
||||||
columnId: string;
|
columnId: string;
|
||||||
index: number;
|
index: number;
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
isDragDisabled: boolean;
|
isDragDisabled: boolean;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue) => React.ReactNode;
|
quickActions: (issue: IIssue) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IssueDetailsBlockProps {
|
interface IssueDetailsBlockProps {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue) => React.ReactNode;
|
quickActions: (issue: IIssue) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = (props) => {
|
const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = (props) => {
|
||||||
const { issue, showEmptyGroup, handleIssues, quickActions, displayProperties, isReadOnly } = props;
|
const { issue, showEmptyGroup, handleIssues, quickActions, isReadOnly, issuesFilter } = props;
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -52,14 +70,17 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{displayProperties && displayProperties?.key && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.key}
|
||||||
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="line-clamp-1 text-xs text-custom-text-300">
|
<div className="line-clamp-1 text-xs text-custom-text-300">
|
||||||
{issue.project_detail.identifier}-{issue.sequence_id}
|
{issue.project_detail.identifier}-{issue.sequence_id}
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute -top-1 right-0 hidden group-hover/kanban-block:block">{quickActions(issue)}</div>
|
<div className="absolute -top-1 right-0 hidden group-hover/kanban-block:block">{quickActions(issue)}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
<div className="line-clamp-2 text-sm font-medium text-custom-text-100" onClick={handleIssuePeekOverview}>
|
<div className="line-clamp-2 text-sm font-medium text-custom-text-100" onClick={handleIssuePeekOverview}>
|
||||||
{issue.name}
|
{issue.name}
|
||||||
@ -68,8 +89,8 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<KanBanProperties
|
<KanBanProperties
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
handleIssues={updateIssue}
|
handleIssues={updateIssue}
|
||||||
displayProperties={displayProperties}
|
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
@ -78,15 +99,7 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateMemo = (prevProps: IssueDetailsBlockProps, nextProps: IssueDetailsBlockProps) => {
|
const KanbanIssueMemoBlock = memo(KanbanIssueDetailsBlock);
|
||||||
if (prevProps.issue !== nextProps.issue) return false;
|
|
||||||
if (!isEqual(prevProps.displayProperties, nextProps.displayProperties)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const KanbanIssueMemoBlock = memo(KanbanIssueDetailsBlock, validateMemo);
|
|
||||||
|
|
||||||
export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
@ -94,11 +107,11 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||||||
columnId,
|
columnId,
|
||||||
index,
|
index,
|
||||||
issue,
|
issue,
|
||||||
|
issuesFilter,
|
||||||
isDragDisabled,
|
isDragDisabled,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
canEditProperties,
|
canEditProperties,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -128,10 +141,10 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<KanbanIssueMemoBlock
|
<KanbanIssueMemoBlock
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
isReadOnly={!canEditIssueProperties}
|
isReadOnly={!canEditIssueProperties}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,21 +1,33 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
//types
|
//types
|
||||||
import { IIssueDisplayProperties, IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { IIssueResponse } from "store_legacy/issues/types";
|
|
||||||
// components
|
// components
|
||||||
import { KanbanIssueBlock } from "components/issues";
|
import { KanbanIssueBlock } from "components/issues";
|
||||||
|
import { IIssueStore } from "store/issue/issue.store";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
|
||||||
interface IssueBlocksListProps {
|
interface IssueBlocksListProps {
|
||||||
sub_group_id: string;
|
sub_group_id: string;
|
||||||
columnId: string;
|
columnId: string;
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: string[];
|
issueIds: string[];
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
isDragDisabled: boolean;
|
isDragDisabled: boolean;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,13 +35,13 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
|||||||
const {
|
const {
|
||||||
sub_group_id,
|
sub_group_id,
|
||||||
columnId,
|
columnId,
|
||||||
issues,
|
issueMap,
|
||||||
issueIds,
|
issueIds,
|
||||||
|
issuesFilter,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
isDragDisabled,
|
isDragDisabled,
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
canEditProperties,
|
canEditProperties,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -38,19 +50,19 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
|||||||
{issueIds && issueIds.length > 0 ? (
|
{issueIds && issueIds.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{issueIds.map((issueId, index) => {
|
{issueIds.map((issueId, index) => {
|
||||||
if (!issues[issueId]) return null;
|
const issue = issueMap.allIssues[issueId];
|
||||||
|
|
||||||
const issue = issues[issueId];
|
if (!issue) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KanbanIssueBlock
|
<KanbanIssueBlock
|
||||||
key={`kanban-issue-block-${issue.id}`}
|
key={`kanban-issue-block-${issue.id}`}
|
||||||
index={index}
|
index={index}
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
columnId={columnId}
|
columnId={columnId}
|
||||||
sub_group_id={sub_group_id}
|
sub_group_id={sub_group_id}
|
||||||
isDragDisabled={isDragDisabled}
|
isDragDisabled={isDragDisabled}
|
||||||
|
@ -5,16 +5,31 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||||
import { KanbanGroup } from "./kanban-group";
|
import { KanbanGroup } from "./kanban-group";
|
||||||
// types
|
// types
|
||||||
import { IIssueDisplayProperties, IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { columnTypes, getKanbanColumns, IKanbanColumn } from "./utils";
|
import { columnTypes, getKanbanColumns, IKanbanColumn } from "./utils";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types";
|
import { IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types";
|
||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
|
import { IIssueStore } from "store/issue/issue.store";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
import { useLabel, useProject, useProjectState } from "hooks/store";
|
||||||
|
|
||||||
export interface IGroupByKanBan {
|
export interface IGroupByKanBan {
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: any;
|
issueIds: any;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
sub_group_by: string | null;
|
sub_group_by: string | null;
|
||||||
group_by: string | null;
|
group_by: string | null;
|
||||||
sub_group_id: string;
|
sub_group_id: string;
|
||||||
@ -22,7 +37,6 @@ export interface IGroupByKanBan {
|
|||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
enableQuickIssueCreate?: boolean;
|
enableQuickIssueCreate?: boolean;
|
||||||
@ -41,8 +55,9 @@ export interface IGroupByKanBan {
|
|||||||
|
|
||||||
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
issues,
|
issueMap,
|
||||||
issueIds,
|
issueIds,
|
||||||
|
issuesFilter,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
group_by,
|
group_by,
|
||||||
sub_group_id = "null",
|
sub_group_id = "null",
|
||||||
@ -50,7 +65,6 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
handleIssues,
|
handleIssues,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
enableQuickIssueCreate,
|
enableQuickIssueCreate,
|
||||||
@ -62,9 +76,12 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
canEditProperties,
|
canEditProperties,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { project, projectLabel, projectMember, projectState } = useMobxStore();
|
//const { projectMember } = useMobxStore();
|
||||||
|
const project = useProject();
|
||||||
|
const projectLabel = useLabel();
|
||||||
|
const projectState = useProjectState();
|
||||||
|
|
||||||
const list = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectMember, projectState);
|
const list = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectState);
|
||||||
|
|
||||||
if (!list) return null;
|
if (!list) return null;
|
||||||
|
|
||||||
@ -99,8 +116,9 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
<KanbanGroup
|
<KanbanGroup
|
||||||
groupId={_list.id}
|
groupId={_list.id}
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
sub_group_by={sub_group_by}
|
sub_group_by={sub_group_by}
|
||||||
group_by={group_by}
|
group_by={group_by}
|
||||||
sub_group_id={sub_group_id}
|
sub_group_id={sub_group_id}
|
||||||
@ -108,7 +126,6 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
enableQuickIssueCreate={enableQuickIssueCreate}
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
@ -124,14 +141,19 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export interface IKanBan {
|
export interface IKanBan {
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues;
|
issueIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
sub_group_by: string | null;
|
sub_group_by: string | null;
|
||||||
group_by: string | null;
|
group_by: string | null;
|
||||||
sub_group_id?: string;
|
sub_group_id?: string;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
@ -151,14 +173,14 @@ export interface IKanBan {
|
|||||||
|
|
||||||
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
issues,
|
issueMap,
|
||||||
issueIds,
|
issueIds,
|
||||||
|
issuesFilter,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
group_by,
|
group_by,
|
||||||
sub_group_id = "null",
|
sub_group_id = "null",
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
@ -176,7 +198,8 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="relative h-full w-full">
|
<div className="relative h-full w-full">
|
||||||
<GroupByKanBan
|
<GroupByKanBan
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
group_by={group_by}
|
group_by={group_by}
|
||||||
sub_group_by={sub_group_by}
|
sub_group_by={sub_group_by}
|
||||||
@ -185,7 +208,6 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
enableQuickIssueCreate={enableQuickIssueCreate}
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
@ -1,14 +1,28 @@
|
|||||||
import { Droppable } from "@hello-pangea/dnd";
|
import { Droppable } from "@hello-pangea/dnd";
|
||||||
//types
|
//types
|
||||||
import { IIssue, IIssueDisplayProperties, IIssueResponse } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
//components
|
//components
|
||||||
import { KanBanQuickAddIssueForm, KanbanIssueBlocksList } from ".";
|
import { KanBanQuickAddIssueForm, KanbanIssueBlocksList } from ".";
|
||||||
|
import { IIssueStore } from "store/issue/issue.store";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
|
||||||
interface IKanbanGroup {
|
interface IKanbanGroup {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: any;
|
issueIds: any;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
sub_group_by: string | null;
|
sub_group_by: string | null;
|
||||||
group_by: string | null;
|
group_by: string | null;
|
||||||
sub_group_id: string;
|
sub_group_id: string;
|
||||||
@ -16,7 +30,6 @@ interface IKanbanGroup {
|
|||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
enableQuickIssueCreate?: boolean;
|
enableQuickIssueCreate?: boolean;
|
||||||
quickAddCallback?: (
|
quickAddCallback?: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
@ -36,14 +49,14 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
|||||||
sub_group_id,
|
sub_group_id,
|
||||||
group_by,
|
group_by,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
issues,
|
issueMap,
|
||||||
|
issuesFilter,
|
||||||
verticalPosition,
|
verticalPosition,
|
||||||
issueIds,
|
issueIds,
|
||||||
isDragDisabled,
|
isDragDisabled,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
canEditProperties,
|
canEditProperties,
|
||||||
enableQuickIssueCreate,
|
enableQuickIssueCreate,
|
||||||
disableIssueCreation,
|
disableIssueCreation,
|
||||||
@ -62,17 +75,17 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
|||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
>
|
>
|
||||||
{issues && !verticalPosition ? (
|
{!verticalPosition ? (
|
||||||
<KanbanIssueBlocksList
|
<KanbanIssueBlocksList
|
||||||
sub_group_id={sub_group_id}
|
sub_group_id={sub_group_id}
|
||||||
columnId={groupId}
|
columnId={groupId}
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
issueIds={issueIds?.[groupId] || []}
|
issueIds={issueIds?.[groupId] || []}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
isDragDisabled={isDragDisabled}
|
isDragDisabled={isDragDisabled}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -10,18 +10,31 @@ import { IssuePropertyAssignee } from "../properties/assignee";
|
|||||||
import { IssuePropertyEstimates } from "../properties/estimates";
|
import { IssuePropertyEstimates } from "../properties/estimates";
|
||||||
import { IssuePropertyDate } from "../properties/date";
|
import { IssuePropertyDate } from "../properties/date";
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
import { IIssue, IIssueDisplayProperties, IState, TIssuePriorities } from "types";
|
import { IIssue, IState, TIssuePriorities } from "types";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
|
|
||||||
export interface IKanBanProperties {
|
export interface IKanBanProperties {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
handleIssues: (issue: IIssue) => void;
|
handleIssues: (issue: IIssue) => void;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
isReadOnly: boolean;
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) => {
|
export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) => {
|
||||||
const { issue, handleIssues, displayProperties, isReadOnly } = props;
|
const { issue, handleIssues, issuesFilter, isReadOnly } = props;
|
||||||
|
|
||||||
const handleState = (state: IState) => {
|
const handleState = (state: IState) => {
|
||||||
handleIssues({ ...issue, state: state.id });
|
handleIssues({ ...issue, state: state.id });
|
||||||
@ -55,7 +68,10 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
<div className="flex flex-wrap items-center gap-2 whitespace-nowrap">
|
<div className="flex flex-wrap items-center gap-2 whitespace-nowrap">
|
||||||
{/* basic properties */}
|
{/* basic properties */}
|
||||||
{/* state */}
|
{/* state */}
|
||||||
{displayProperties && displayProperties?.state && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties && displayProperties?.state}
|
||||||
|
>
|
||||||
<IssuePropertyState
|
<IssuePropertyState
|
||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.state || null}
|
value={issue?.state || null}
|
||||||
@ -64,20 +80,27 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* priority */}
|
{/* priority */}
|
||||||
{displayProperties && displayProperties?.priority && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties && displayProperties?.priority}
|
||||||
|
>
|
||||||
<IssuePropertyPriority
|
<IssuePropertyPriority
|
||||||
value={issue?.priority || null}
|
value={issue?.priority || null}
|
||||||
onChange={handlePriority}
|
onChange={handlePriority}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* label */}
|
{/* label */}
|
||||||
{displayProperties && displayProperties?.labels && (
|
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties && displayProperties?.labels}
|
||||||
|
>
|
||||||
<IssuePropertyLabels
|
<IssuePropertyLabels
|
||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.labels || null}
|
value={issue?.labels || null}
|
||||||
@ -86,30 +109,39 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* start date */}
|
{/* start date */}
|
||||||
{displayProperties && displayProperties?.start_date && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.start_date}
|
||||||
|
>
|
||||||
<IssuePropertyDate
|
<IssuePropertyDate
|
||||||
value={issue?.start_date || null}
|
value={issue?.start_date || null}
|
||||||
onChange={(date: string) => handleStartDate(date)}
|
onChange={(date: string) => handleStartDate(date)}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
type="start_date"
|
type="start_date"
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* target/due date */}
|
{/* target/due date */}
|
||||||
{displayProperties && displayProperties?.due_date && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.due_date}
|
||||||
|
>
|
||||||
<IssuePropertyDate
|
<IssuePropertyDate
|
||||||
value={issue?.target_date || null}
|
value={issue?.target_date || null}
|
||||||
onChange={(date: string) => handleTargetDate(date)}
|
onChange={(date: string) => handleTargetDate(date)}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
type="target_date"
|
type="target_date"
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* assignee */}
|
{/* assignee */}
|
||||||
{displayProperties && displayProperties?.assignee && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.assignee}
|
||||||
|
>
|
||||||
<IssuePropertyAssignee
|
<IssuePropertyAssignee
|
||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.assignees || null}
|
value={issue?.assignees || null}
|
||||||
@ -119,10 +151,13 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* estimates */}
|
{/* estimates */}
|
||||||
{displayProperties && displayProperties?.estimate && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.estimate}
|
||||||
|
>
|
||||||
<IssuePropertyEstimates
|
<IssuePropertyEstimates
|
||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.estimate_point || null}
|
value={issue?.estimate_point || null}
|
||||||
@ -130,38 +165,49 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* extra render properties */}
|
{/* extra render properties */}
|
||||||
{/* sub-issues */}
|
{/* sub-issues */}
|
||||||
{displayProperties && displayProperties?.sub_issue_count && !!issue?.sub_issues_count && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.sub_issue_count && !!issue?.sub_issues_count}
|
||||||
|
>
|
||||||
<Tooltip tooltipHeading="Sub-issues" tooltipContent={`${issue.sub_issues_count}`}>
|
<Tooltip tooltipHeading="Sub-issues" tooltipContent={`${issue.sub_issues_count}`}>
|
||||||
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||||
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||||
<div className="text-xs">{issue.sub_issues_count}</div>
|
<div className="text-xs">{issue.sub_issues_count}</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* attachments */}
|
{/* attachments */}
|
||||||
{displayProperties && displayProperties?.attachment_count && !!issue?.attachment_count && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) =>
|
||||||
|
displayProperties?.attachment_count && !!issue?.attachment_count
|
||||||
|
}
|
||||||
|
>
|
||||||
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
|
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
|
||||||
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||||
<Paperclip className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
<Paperclip className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||||
<div className="text-xs">{issue.attachment_count}</div>
|
<div className="text-xs">{issue.attachment_count}</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* link */}
|
{/* link */}
|
||||||
{displayProperties && displayProperties?.link && !!issue?.link_count && (
|
<WithDisplayPropertiesHOC
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
|
getShouldRenderProperty={(displayProperties) => displayProperties?.link && !!issue?.link_count}
|
||||||
|
>
|
||||||
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
|
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
|
||||||
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1">
|
||||||
<Link className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
<Link className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||||
<div className="text-xs">{issue.link_count}</div>
|
<div className="text-xs">{issue.link_count}</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
</WithDisplayPropertiesHOC>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -11,67 +11,45 @@ import { IIssue } from "types";
|
|||||||
// constants
|
// constants
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
import { IGroupedIssues, IIssueResponse, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types";
|
|
||||||
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
|
import { useIssues } from "hooks/store/use-issues";
|
||||||
|
|
||||||
export interface IKanBanLayout {}
|
export interface IKanBanLayout {}
|
||||||
|
|
||||||
export const KanBanLayout: React.FC = observer(() => {
|
export const KanBanLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const {
|
const { issueKanBanView: issueKanBanViewStore } = useMobxStore();
|
||||||
projectIssues: issueStore,
|
|
||||||
projectIssuesFilter: issuesFilterStore,
|
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||||
issueKanBanView: issueKanBanViewStore,
|
|
||||||
kanBanHelpers: kanBanHelperStore,
|
|
||||||
} = useMobxStore();
|
|
||||||
|
|
||||||
const issueActions = useMemo(
|
const issueActions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
await issues.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
await issues.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[issueStore]
|
[issues]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDragDrop = async (
|
|
||||||
source: any,
|
|
||||||
destination: any,
|
|
||||||
subGroupBy: string | null,
|
|
||||||
groupBy: string | null,
|
|
||||||
issues: IIssueResponse | undefined,
|
|
||||||
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
|
||||||
) =>
|
|
||||||
await kanBanHelperStore.handleDragDrop(
|
|
||||||
source,
|
|
||||||
destination,
|
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
issueStore,
|
|
||||||
subGroupBy,
|
|
||||||
groupBy,
|
|
||||||
issues,
|
|
||||||
issueWithIds
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
issuesFilterStore={issuesFilterStore}
|
issues={issues}
|
||||||
issueStore={issueStore}
|
issuesFilter={issuesFilter}
|
||||||
kanbanViewStore={issueKanBanViewStore}
|
kanbanViewStore={issueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
currentStore={EProjectStore.PROJECT}
|
currentStore={EProjectStore.PROJECT}
|
||||||
handleDragDrop={handleDragDrop}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
//mobx
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// components
|
// components
|
||||||
import { KanBan } from "./default";
|
import { KanBan } from "./default";
|
||||||
import { HeaderSubGroupByCard } from "./headers/sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./headers/sub-group-by-card";
|
||||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayProperties } from "types";
|
import { IIssue } from "types";
|
||||||
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types";
|
import { IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store_legacy/issues/types";
|
||||||
// constants
|
// constants
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
import { IKanbanColumn, columnTypes, getKanbanColumns } from "./utils";
|
import { IKanbanColumn, columnTypes, getKanbanColumns } from "./utils";
|
||||||
|
import { IIssueStore } from "store/issue/issue.store";
|
||||||
|
import { useLabel, useProject, useProjectState } from "hooks/store";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
|
||||||
interface ISubGroupSwimlaneHeader {
|
interface ISubGroupSwimlaneHeader {
|
||||||
issueIds: any;
|
issueIds: any;
|
||||||
@ -51,12 +58,17 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: any;
|
issueIds: any;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
@ -75,14 +87,14 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
|||||||
}
|
}
|
||||||
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
issues,
|
issueMap,
|
||||||
issueIds,
|
issueIds,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
group_by,
|
group_by,
|
||||||
list,
|
list,
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
issuesFilter,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
@ -124,14 +136,14 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
{!kanBanToggle?.subgroupByIssuesVisibility.includes(_list.id) && (
|
{!kanBanToggle?.subgroupByIssuesVisibility.includes(_list.id) && (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<KanBan
|
<KanBan
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
issueIds={issueIds?.[_list.id]}
|
issueIds={issueIds?.[_list.id]}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
sub_group_by={sub_group_by}
|
sub_group_by={sub_group_by}
|
||||||
group_by={group_by}
|
group_by={group_by}
|
||||||
sub_group_id={_list.id}
|
sub_group_id={_list.id}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
@ -150,13 +162,18 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export interface IKanBanSwimLanes {
|
export interface IKanBanSwimLanes {
|
||||||
issues: IIssueResponse;
|
issueMap: IIssueStore;
|
||||||
issueIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues;
|
issueIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues;
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
sub_group_by: string | null;
|
sub_group_by: string | null;
|
||||||
group_by: string | null;
|
group_by: string | null;
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
@ -177,13 +194,13 @@ export interface IKanBanSwimLanes {
|
|||||||
|
|
||||||
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
issues,
|
issueMap,
|
||||||
issueIds,
|
issueIds,
|
||||||
|
issuesFilter,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
group_by,
|
group_by,
|
||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
@ -196,16 +213,12 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
viewId,
|
viewId,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { project, projectLabel, projectMember, projectState } = useMobxStore();
|
const project = useProject();
|
||||||
|
const projectLabel = useLabel();
|
||||||
|
const projectState = useProjectState();
|
||||||
|
|
||||||
const groupByList = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectMember, projectState);
|
const groupByList = getKanbanColumns(group_by as columnTypes, project, projectLabel, projectState);
|
||||||
const subGroupByList = getKanbanColumns(
|
const subGroupByList = getKanbanColumns(sub_group_by as columnTypes, project, projectLabel, projectState);
|
||||||
sub_group_by as columnTypes,
|
|
||||||
project,
|
|
||||||
projectLabel,
|
|
||||||
projectMember,
|
|
||||||
projectState
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!groupByList || !subGroupByList) return null;
|
if (!groupByList || !subGroupByList) return null;
|
||||||
|
|
||||||
@ -224,14 +237,14 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
|
|
||||||
{sub_group_by && (
|
{sub_group_by && (
|
||||||
<SubGroupSwimlane
|
<SubGroupSwimlane
|
||||||
issues={issues}
|
issueMap={issueMap}
|
||||||
list={subGroupByList}
|
list={subGroupByList}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
|
issuesFilter={issuesFilter}
|
||||||
group_by={group_by}
|
group_by={group_by}
|
||||||
sub_group_by={sub_group_by}
|
sub_group_by={sub_group_by}
|
||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
|
import { DraggableLocation } from "@hello-pangea/dnd";
|
||||||
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
||||||
import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue";
|
import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue";
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { IProjectLabelStore, IProjectMemberStore, IProjectStateStore, IProjectStore } from "store_legacy/project";
|
import { IIssueStore } from "store/issue/issue.store";
|
||||||
import { IIssue } from "types";
|
import { IProjectIssues } from "store/issue/project";
|
||||||
|
import { ILabelRootStore } from "store/label";
|
||||||
|
import { IProjectStore } from "store/project/project.store";
|
||||||
|
import { IStateStore } from "store/state.store";
|
||||||
|
import { IGroupedIssues, IIssue, ISubGroupedIssues, TUnGroupedIssues } from "types";
|
||||||
|
|
||||||
export type columnTypes =
|
export type columnTypes =
|
||||||
| "project"
|
| "project"
|
||||||
@ -24,9 +29,9 @@ export interface IKanbanColumn {
|
|||||||
export const getKanbanColumns = (
|
export const getKanbanColumns = (
|
||||||
groupBy: columnTypes | null,
|
groupBy: columnTypes | null,
|
||||||
project: IProjectStore,
|
project: IProjectStore,
|
||||||
projectLabel: IProjectLabelStore,
|
projectLabel: ILabelRootStore,
|
||||||
projectMember: IProjectMemberStore,
|
projectState: IStateStore
|
||||||
projectState: IProjectStateStore
|
//projectMember?: IProjectMemberStore,
|
||||||
): IKanbanColumn[] | undefined => {
|
): IKanbanColumn[] | undefined => {
|
||||||
switch (groupBy) {
|
switch (groupBy) {
|
||||||
case "project":
|
case "project":
|
||||||
@ -39,27 +44,32 @@ export const getKanbanColumns = (
|
|||||||
return getPriorityColumns();
|
return getPriorityColumns();
|
||||||
case "labels":
|
case "labels":
|
||||||
return getLabelsColumns(projectLabel);
|
return getLabelsColumns(projectLabel);
|
||||||
case "assignees":
|
// case "assignees":
|
||||||
return getAssigneeColumns(projectMember);
|
// return getAssigneeColumns(projectMember);
|
||||||
case "created_by":
|
// case "created_by":
|
||||||
return getCreatedByColumns(projectMember);
|
// return getCreatedByColumns(projectMember);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectColumns = (project: IProjectStore): IKanbanColumn[] | undefined => {
|
const getProjectColumns = (project: IProjectStore): IKanbanColumn[] | undefined => {
|
||||||
const { workspaceProjects: projects } = project;
|
const { workspaceProjects: projectIds, projectMap } = project;
|
||||||
|
|
||||||
if (!projects) return;
|
if (!projectIds) return;
|
||||||
|
|
||||||
return projects.map((project) => ({
|
return projectIds.map((projectId) => {
|
||||||
id: project.id,
|
const project = projectMap[projectId];
|
||||||
name: project.name,
|
|
||||||
Icon: <div className="w-6 h-6">{renderEmoji(project.emoji || "")}</div>,
|
if (project)
|
||||||
payload: { project: project.id },
|
return {
|
||||||
}));
|
id: project.id,
|
||||||
|
name: project.name,
|
||||||
|
Icon: <div className="w-6 h-6">{renderEmoji(project.emoji || "")}</div>,
|
||||||
|
payload: { project: project.id },
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStateColumns = (projectState: IProjectStateStore): IKanbanColumn[] | undefined => {
|
const getStateColumns = (projectState: IStateStore): IKanbanColumn[] | undefined => {
|
||||||
const { projectStates } = projectState;
|
const { projectStates } = projectState;
|
||||||
if (!projectStates) return;
|
if (!projectStates) return;
|
||||||
|
|
||||||
@ -101,8 +111,10 @@ const getPriorityColumns = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLabelsColumns = (projectLabel: IProjectLabelStore) => {
|
const getLabelsColumns = (projectLabel: ILabelRootStore) => {
|
||||||
const { projectLabels } = projectLabel;
|
const {
|
||||||
|
project: { projectLabels },
|
||||||
|
} = projectLabel;
|
||||||
|
|
||||||
if (!projectLabels) return;
|
if (!projectLabels) return;
|
||||||
|
|
||||||
@ -118,32 +130,185 @@ const getLabelsColumns = (projectLabel: IProjectLabelStore) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAssigneeColumns = (projectMember: IProjectMemberStore) => {
|
// const getAssigneeColumns = (projectMember: IProjectMemberStore) => {
|
||||||
const { projectMembers: users } = projectMember;
|
// const { projectMembers: users } = projectMember;
|
||||||
if (!users) return;
|
// if (!users) return;
|
||||||
|
|
||||||
return users.map((user) => {
|
// return users.map((user) => {
|
||||||
const member = user.member;
|
// const member = user.member;
|
||||||
return {
|
// return {
|
||||||
id: member?.id,
|
// id: member?.id,
|
||||||
name: member?.display_name || "",
|
// name: member?.display_name || "",
|
||||||
Icon: <Avatar name={member?.display_name} src={member?.avatar} size="md" />,
|
// Icon: <Avatar name={member?.display_name} src={member?.avatar} size="md" />,
|
||||||
payload: { assignees: [member?.id] },
|
// payload: { assignees: [member?.id] },
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const getCreatedByColumns = (projectMember: IProjectMemberStore) => {
|
||||||
|
// const { projectMembers: users } = projectMember;
|
||||||
|
// if (!users) return;
|
||||||
|
|
||||||
|
// return users.map((user) => {
|
||||||
|
// const member = user.member;
|
||||||
|
// return {
|
||||||
|
// id: member?.id,
|
||||||
|
// name: member?.display_name || "",
|
||||||
|
// Icon: <Avatar name={member?.display_name} src={member?.avatar} size="md" />,
|
||||||
|
// payload: { created_by: member?.id },
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
export const handleDragDrop = async (
|
||||||
|
source: DraggableLocation | null,
|
||||||
|
destination: DraggableLocation | null,
|
||||||
|
workspaceSlug: string,
|
||||||
|
projectId: string, // projectId for all views or user id in profile issues
|
||||||
|
store: IProjectIssues,
|
||||||
|
subGroupBy: string | null,
|
||||||
|
groupBy: string | null,
|
||||||
|
issueMap: IIssueStore | undefined,
|
||||||
|
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined,
|
||||||
|
viewId: string | null = null // it can be moduleId, cycleId
|
||||||
|
) => {
|
||||||
|
if (!issueMap || !issueWithIds || !source || !destination) return;
|
||||||
|
|
||||||
|
let updateIssue: any = {};
|
||||||
|
|
||||||
|
const sourceColumnId = (source?.droppableId && source?.droppableId.split("__")) || null;
|
||||||
|
const destinationColumnId = (destination?.droppableId && destination?.droppableId.split("__")) || null;
|
||||||
|
|
||||||
|
if (!sourceColumnId || !destinationColumnId) return;
|
||||||
|
|
||||||
|
const sourceGroupByColumnId = sourceColumnId[0] || null;
|
||||||
|
const destinationGroupByColumnId = destinationColumnId[0] || null;
|
||||||
|
|
||||||
|
const sourceSubGroupByColumnId = sourceColumnId[1] || null;
|
||||||
|
const destinationSubGroupByColumnId = destinationColumnId[1] || null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!workspaceSlug ||
|
||||||
|
!projectId ||
|
||||||
|
!groupBy ||
|
||||||
|
!sourceGroupByColumnId ||
|
||||||
|
!destinationGroupByColumnId ||
|
||||||
|
!sourceSubGroupByColumnId ||
|
||||||
|
!sourceGroupByColumnId
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (destinationGroupByColumnId === "issue-trash-box") {
|
||||||
|
const sourceIssues: string[] = subGroupBy
|
||||||
|
? (issueWithIds as ISubGroupedIssues)[sourceSubGroupByColumnId][sourceGroupByColumnId]
|
||||||
|
: (issueWithIds as IGroupedIssues)[sourceGroupByColumnId];
|
||||||
|
|
||||||
|
const [removed] = sourceIssues.splice(source.index, 1);
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
if (viewId) return await store?.removeIssue(workspaceSlug, projectId, removed); //, viewId);
|
||||||
|
else return await 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];
|
||||||
|
|
||||||
|
const [removed] = sourceIssues.splice(source.index, 1);
|
||||||
|
const removedIssueDetail = issueMap.allIssues[removed];
|
||||||
|
|
||||||
|
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
|
||||||
|
updateIssue = {
|
||||||
|
id: removedIssueDetail?.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
// for both horizontal and vertical dnd
|
||||||
|
updateIssue = {
|
||||||
|
...updateIssue,
|
||||||
|
...handleSortOrder(destinationIssues, destination.index, issueMap),
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// for both horizontal and vertical dnd
|
||||||
|
updateIssue = {
|
||||||
|
...updateIssue,
|
||||||
|
...handleSortOrder(destinationIssues, destination.index, issueMap),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) return await store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue); //, viewId);
|
||||||
|
else return await store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCreatedByColumns = (projectMember: IProjectMemberStore) => {
|
const handleSortOrder = (destinationIssues: string[], destinationIndex: number, issueMap: IIssueStore) => {
|
||||||
const { projectMembers: users } = projectMember;
|
const sortOrderDefaultValue = 65535;
|
||||||
if (!users) return;
|
let currentIssueState = {};
|
||||||
|
|
||||||
return users.map((user) => {
|
if (destinationIssues && destinationIssues.length > 0) {
|
||||||
const member = user.member;
|
if (destinationIndex === 0) {
|
||||||
return {
|
const destinationIssueId = destinationIssues[destinationIndex];
|
||||||
id: member?.id,
|
currentIssueState = {
|
||||||
name: member?.display_name || "",
|
...currentIssueState,
|
||||||
Icon: <Avatar name={member?.display_name} src={member?.avatar} size="md" />,
|
sort_order: issueMap.allIssues[destinationIssueId].sort_order - sortOrderDefaultValue,
|
||||||
payload: { created_by: member?.id },
|
};
|
||||||
|
} else if (destinationIndex === destinationIssues.length) {
|
||||||
|
const destinationIssueId = destinationIssues[destinationIndex - 1];
|
||||||
|
currentIssueState = {
|
||||||
|
...currentIssueState,
|
||||||
|
sort_order: issueMap.allIssues[destinationIssueId].sort_order + sortOrderDefaultValue,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const destinationTopIssueId = destinationIssues[destinationIndex - 1];
|
||||||
|
const destinationBottomIssueId = destinationIssues[destinationIndex];
|
||||||
|
currentIssueState = {
|
||||||
|
...currentIssueState,
|
||||||
|
sort_order:
|
||||||
|
(issueMap.allIssues[destinationTopIssueId].sort_order +
|
||||||
|
issueMap.allIssues[destinationBottomIssueId].sort_order) /
|
||||||
|
2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentIssueState = {
|
||||||
|
...currentIssueState,
|
||||||
|
sort_order: sortOrderDefaultValue,
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return currentIssueState;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
} from "store_legacy/issues";
|
||||||
|
import { IIssueDisplayProperties } from "types";
|
||||||
|
|
||||||
|
interface IWithDisplayPropertiesHOC {
|
||||||
|
issuesFilter:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
|
getShouldRenderProperty: (displayProperties: IIssueDisplayProperties) => boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
export const WithDisplayPropertiesHOC = observer(
|
||||||
|
({ issuesFilter, getShouldRenderProperty, children }: IWithDisplayPropertiesHOC) => {
|
||||||
|
const displayProperties = issuesFilter.issueFilters.displayProperties;
|
||||||
|
|
||||||
|
const shouldRenderProperty = getShouldRenderProperty(displayProperties);
|
||||||
|
|
||||||
|
if (!shouldRenderProperty) return null;
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
);
|
@ -1,11 +1,8 @@
|
|||||||
import React from "react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// TODO: update this
|
|
||||||
import useStoreIssues from "hooks/use-store-issues";
|
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
ListLayout,
|
ListLayout,
|
||||||
@ -17,6 +14,8 @@ import {
|
|||||||
ProjectEmptyState,
|
ProjectEmptyState,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
|
import { useIssues } from "hooks/store/use-issues";
|
||||||
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
||||||
export const ProjectLayoutRoot: React.FC = observer(() => {
|
export const ProjectLayoutRoot: React.FC = observer(() => {
|
||||||
@ -25,16 +24,20 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
|
|||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectIssues: { loader, getIssues, fetchIssues },
|
issues: { loader, getIssues, fetchIssues },
|
||||||
projectIssuesFilter: { issueFilters, fetchFilters },
|
issuesFilter: { issueFilters, fetchFilters },
|
||||||
} = useMobxStore();
|
} = useIssues(EIssuesStoreType.PROJECT);
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => {
|
useSWR(
|
||||||
if (workspaceSlug && projectId) {
|
workspaceSlug && projectId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null,
|
||||||
await fetchFilters(workspaceSlug, projectId);
|
async () => {
|
||||||
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
if (workspaceSlug && projectId) {
|
||||||
}
|
await fetchFilters(workspaceSlug, projectId);
|
||||||
});
|
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ revalidateOnFocus: false, refreshInterval: 600000, revalidateOnMount: true }
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: update this
|
// TODO: update this
|
||||||
// const {
|
// const {
|
||||||
|
@ -408,3 +408,13 @@ export const groupReactionEmojis = (reactions: any) => {
|
|||||||
|
|
||||||
return _groupedEmojis;
|
return _groupedEmojis;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum EIssuesStoreType {
|
||||||
|
MODULE,
|
||||||
|
CYClE,
|
||||||
|
PROJECT,
|
||||||
|
VIEW,
|
||||||
|
DRAFT,
|
||||||
|
ARCHIVED,
|
||||||
|
GLOBAL,
|
||||||
|
}
|
||||||
|
20
web/hooks/store/use-issues.ts
Normal file
20
web/hooks/store/use-issues.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { useContext } from "react";
|
||||||
|
// mobx store
|
||||||
|
import { MobxStoreContext } from "lib/mobx/store-provider";
|
||||||
|
// types
|
||||||
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
|
|
||||||
|
export const useIssues = (storeType?: EIssuesStoreType) => {
|
||||||
|
const context = useContext(MobxStoreContext);
|
||||||
|
if (context === undefined) throw new Error("useMobxStore must be used within MobxStoreProvider");
|
||||||
|
switch (storeType) {
|
||||||
|
case EIssuesStoreType.PROJECT:
|
||||||
|
return {
|
||||||
|
issues: context.issue.projectIssues,
|
||||||
|
issuesFilter: context.issue.projectIssuesFilter,
|
||||||
|
issueMap: context.issue.issues,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return { issueMap: context.issue.issues };
|
||||||
|
}
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
// mobx store
|
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
|
// mobx store
|
||||||
|
|
||||||
let rootStore: RootStore = new RootStore();
|
let rootStore: RootStore = new RootStore();
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export class IssueStore implements IIssueStore {
|
|||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observable
|
// observable
|
||||||
allIssues: observable.ref,
|
allIssues: observable,
|
||||||
// actions
|
// actions
|
||||||
addIssue: action,
|
addIssue: action,
|
||||||
updateIssue: action,
|
updateIssue: action,
|
||||||
|
76
web/store/issue/issue_kanban_view.store.ts
Normal file
76
web/store/issue/issue_kanban_view.store.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { IssueRootStore } from "./root.store";
|
||||||
|
// types
|
||||||
|
|
||||||
|
export interface IIssueKanBanViewStore {
|
||||||
|
kanBanToggle: {
|
||||||
|
groupByHeaderMinMax: string[];
|
||||||
|
subgroupByIssuesVisibility: string[];
|
||||||
|
};
|
||||||
|
// computed
|
||||||
|
canUserDragDrop: boolean;
|
||||||
|
canUserDragDropVertically: boolean;
|
||||||
|
canUserDragDropHorizontally: boolean;
|
||||||
|
// actions
|
||||||
|
handleKanBanToggle: (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
||||||
|
kanBanToggle: {
|
||||||
|
groupByHeaderMinMax: string[];
|
||||||
|
subgroupByIssuesVisibility: string[];
|
||||||
|
} = { groupByHeaderMinMax: [], subgroupByIssuesVisibility: [] };
|
||||||
|
// root store
|
||||||
|
rootStore;
|
||||||
|
|
||||||
|
constructor(_rootStore: IssueRootStore) {
|
||||||
|
makeObservable(this, {
|
||||||
|
kanBanToggle: observable,
|
||||||
|
// computed
|
||||||
|
canUserDragDrop: computed,
|
||||||
|
canUserDragDropVertically: computed,
|
||||||
|
canUserDragDropHorizontally: computed,
|
||||||
|
|
||||||
|
// actions
|
||||||
|
handleKanBanToggle: action,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rootStore = _rootStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canUserDragDrop() {
|
||||||
|
return true;
|
||||||
|
if (this.rootStore.issueDetail.peekId) return false;
|
||||||
|
if (
|
||||||
|
this.rootStore?.issueFilter?.userDisplayFilters?.order_by &&
|
||||||
|
this.rootStore?.issueFilter?.userDisplayFilters?.order_by === "sort_order" &&
|
||||||
|
this.rootStore?.issueFilter?.userDisplayFilters?.group_by &&
|
||||||
|
["state", "priority"].includes(this.rootStore?.issueFilter?.userDisplayFilters?.group_by)
|
||||||
|
) {
|
||||||
|
if (!this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by) return true;
|
||||||
|
if (
|
||||||
|
this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by &&
|
||||||
|
["state", "priority"].includes(this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by)
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canUserDragDropVertically() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canUserDragDropHorizontally() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {
|
||||||
|
this.kanBanToggle = {
|
||||||
|
...this.kanBanToggle,
|
||||||
|
[toggle]: this.kanBanToggle[toggle].includes(value)
|
||||||
|
? this.kanBanToggle[toggle].filter((v) => v !== value)
|
||||||
|
: [...this.kanBanToggle[toggle], value],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
@ -14,6 +14,7 @@ import {
|
|||||||
TIssueGroupByOptions,
|
TIssueGroupByOptions,
|
||||||
TLoader,
|
TLoader,
|
||||||
TUnGroupedIssues,
|
TUnGroupedIssues,
|
||||||
|
ViewFlags,
|
||||||
} from "types";
|
} from "types";
|
||||||
|
|
||||||
export interface IProjectIssues {
|
export interface IProjectIssues {
|
||||||
@ -28,6 +29,7 @@ export interface IProjectIssues {
|
|||||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<IIssue>) => Promise<IIssue>;
|
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<IIssue>) => Promise<IIssue>;
|
||||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<IIssue>;
|
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<IIssue>;
|
||||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: IIssue) => Promise<IIssue>;
|
quickAddIssue: (workspaceSlug: string, projectId: string, data: IIssue) => Promise<IIssue>;
|
||||||
|
viewFlags: ViewFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
||||||
@ -38,6 +40,12 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
|||||||
issueService;
|
issueService;
|
||||||
// root store
|
// root store
|
||||||
rootIssueStore: IIssueRootStore;
|
rootIssueStore: IIssueRootStore;
|
||||||
|
//viewData
|
||||||
|
viewFlags = {
|
||||||
|
enableQuickAdd: true,
|
||||||
|
enableIssueCreation: true,
|
||||||
|
enableInlineEditing: true,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(_rootStore: IIssueRootStore) {
|
constructor(_rootStore: IIssueRootStore) {
|
||||||
super(_rootStore);
|
super(_rootStore);
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
} from "./project-views";
|
} from "./project-views";
|
||||||
import { IArchivedIssuesFilter, ArchivedIssuesFilter, IArchivedIssues, ArchivedIssues } from "./archived";
|
import { IArchivedIssuesFilter, ArchivedIssuesFilter, IArchivedIssues, ArchivedIssues } from "./archived";
|
||||||
import { IDraftIssuesFilter, DraftIssuesFilter, IDraftIssues, DraftIssues } from "./draft";
|
import { IDraftIssuesFilter, DraftIssuesFilter, IDraftIssues, DraftIssues } from "./draft";
|
||||||
|
import { IIssueKanBanViewStore, IssueKanBanViewStore } from "./issue_kanban_view.store";
|
||||||
|
|
||||||
export interface IIssueRootStore {
|
export interface IIssueRootStore {
|
||||||
currentUserId: string | undefined;
|
currentUserId: string | undefined;
|
||||||
@ -59,6 +60,8 @@ export interface IIssueRootStore {
|
|||||||
|
|
||||||
draftIssuesFilter: IDraftIssuesFilter;
|
draftIssuesFilter: IDraftIssuesFilter;
|
||||||
draftIssues: IDraftIssues;
|
draftIssues: IDraftIssues;
|
||||||
|
|
||||||
|
issueKanBanView: IIssueKanBanViewStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IssueRootStore {
|
export class IssueRootStore {
|
||||||
@ -102,6 +105,8 @@ export class IssueRootStore {
|
|||||||
draftIssuesFilter: IDraftIssuesFilter;
|
draftIssuesFilter: IDraftIssuesFilter;
|
||||||
draftIssues: IDraftIssues;
|
draftIssues: IDraftIssues;
|
||||||
|
|
||||||
|
issueKanBanView: IIssueKanBanViewStore;
|
||||||
|
|
||||||
constructor(rootStore: RootStore) {
|
constructor(rootStore: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
currentUserId: observable.ref,
|
currentUserId: observable.ref,
|
||||||
@ -160,5 +165,7 @@ export class IssueRootStore {
|
|||||||
|
|
||||||
this.draftIssuesFilter = new DraftIssuesFilter(this);
|
this.draftIssuesFilter = new DraftIssuesFilter(this);
|
||||||
this.draftIssues = new DraftIssues();
|
this.draftIssues = new DraftIssues();
|
||||||
|
|
||||||
|
this.issueKanBanView = new IssueKanBanViewStore(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { IssueRootStore, IIssueRootStore } from "./issue/root.store";
|
|||||||
import { IStateStore, StateStore } from "./state.store";
|
import { IStateStore, StateStore } from "./state.store";
|
||||||
import { IPageStore, PageStore } from "./page.store";
|
import { IPageStore, PageStore } from "./page.store";
|
||||||
import { ILabelRootStore, LabelRootStore } from "./label";
|
import { ILabelRootStore, LabelRootStore } from "./label";
|
||||||
|
|
||||||
enableStaticRendering(typeof window === "undefined");
|
enableStaticRendering(typeof window === "undefined");
|
||||||
|
|
||||||
export class RootStore {
|
export class RootStore {
|
||||||
|
6
web/types/issues.d.ts
vendored
6
web/types/issues.d.ts
vendored
@ -272,3 +272,9 @@ export type TUnGroupedIssues = string[];
|
|||||||
export interface IIssueResponse {
|
export interface IIssueResponse {
|
||||||
[issue_id: string]: IIssue;
|
[issue_id: string]: IIssue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ViewFlags {
|
||||||
|
enableQuickAdd: boolean;
|
||||||
|
enableIssueCreation: boolean;
|
||||||
|
enableInlineEditing: boolean;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user