mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-749] fix: rendering empty states with "showEmptyGroup" filter in issue grouping (#3954)
* fix: Fixed show empty states in groupBy and subGroupBy in kanban * lint: lint issue resolved
This commit is contained in:
parent
6ec9c64f7c
commit
898cf98be3
@ -58,6 +58,7 @@ export interface IGroupByKanBan {
|
|||||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
showEmptyGroup?: boolean;
|
showEmptyGroup?: boolean;
|
||||||
|
subGroupIssueHeaderCount?: (listId: string) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||||
@ -83,6 +84,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
scrollableContainerRef,
|
scrollableContainerRef,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
showEmptyGroup = true,
|
showEmptyGroup = true,
|
||||||
|
subGroupIssueHeaderCount,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const member = useMember();
|
const member = useMember();
|
||||||
@ -97,17 +99,27 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
|
|
||||||
if (!list) return null;
|
if (!list) return null;
|
||||||
|
|
||||||
const groupWithIssues = list.filter((_list) => (issueIds as TGroupedIssues)?.[_list.id]?.length > 0);
|
const visibilityGroupBy = (_list: IGroupByColumn): { showGroup: boolean; showIssues: boolean } => {
|
||||||
|
|
||||||
const groupList = showEmptyGroup ? list : groupWithIssues;
|
|
||||||
|
|
||||||
const visibilityGroupBy = (_list: IGroupByColumn) => {
|
|
||||||
if (sub_group_by) {
|
if (sub_group_by) {
|
||||||
if (kanbanFilters?.sub_group_by.includes(_list.id)) return true;
|
const groupVisibility = {
|
||||||
return false;
|
showGroup: true,
|
||||||
|
showIssues: true,
|
||||||
|
};
|
||||||
|
if (!showEmptyGroup) {
|
||||||
|
groupVisibility.showGroup = subGroupIssueHeaderCount ? subGroupIssueHeaderCount(_list.id) > 0 : true;
|
||||||
|
}
|
||||||
|
return groupVisibility;
|
||||||
} else {
|
} else {
|
||||||
if (kanbanFilters?.group_by.includes(_list.id)) return true;
|
const groupVisibility = {
|
||||||
return false;
|
showGroup: true,
|
||||||
|
showIssues: true,
|
||||||
|
};
|
||||||
|
if (!showEmptyGroup) {
|
||||||
|
if ((issueIds as TGroupedIssues)?.[_list.id]?.length > 0) groupVisibility.showGroup = true;
|
||||||
|
else groupVisibility.showGroup = false;
|
||||||
|
}
|
||||||
|
if (kanbanFilters?.group_by.includes(_list.id)) groupVisibility.showIssues = false;
|
||||||
|
return groupVisibility;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,13 +127,18 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`relative w-full flex gap-2 ${sub_group_by ? "h-full" : "h-full"}`}>
|
<div className={`relative w-full flex gap-2 ${sub_group_by ? "h-full" : "h-full"}`}>
|
||||||
{groupList &&
|
{list &&
|
||||||
groupList.length > 0 &&
|
list.length > 0 &&
|
||||||
groupList.map((_list: IGroupByColumn) => {
|
list.map((_list: IGroupByColumn) => {
|
||||||
const groupByVisibilityToggle = visibilityGroupBy(_list);
|
const groupByVisibilityToggle = visibilityGroupBy(_list);
|
||||||
|
|
||||||
|
if (groupByVisibilityToggle.showGroup === false) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className={`relative flex flex-shrink-0 flex-col group ${groupByVisibilityToggle ? `` : `w-[350px]`}`}>
|
<div
|
||||||
|
className={`relative flex flex-shrink-0 flex-col group ${
|
||||||
|
groupByVisibilityToggle.showIssues ? `w-[350px]` : ``
|
||||||
|
} `}
|
||||||
|
>
|
||||||
{sub_group_by === null && (
|
{sub_group_by === null && (
|
||||||
<div className="flex-shrink-0 sticky top-0 z-[2] w-full bg-custom-background-90 py-1">
|
<div className="flex-shrink-0 sticky top-0 z-[2] w-full bg-custom-background-90 py-1">
|
||||||
<HeaderGroupByCard
|
<HeaderGroupByCard
|
||||||
@ -141,7 +158,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!groupByVisibilityToggle && (
|
{groupByVisibilityToggle.showIssues && (
|
||||||
<KanbanGroup
|
<KanbanGroup
|
||||||
groupId={_list.id}
|
groupId={_list.id}
|
||||||
issuesMap={issuesMap}
|
issuesMap={issuesMap}
|
||||||
@ -159,7 +176,6 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
disableIssueCreation={disableIssueCreation}
|
disableIssueCreation={disableIssueCreation}
|
||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
groupByVisibilityToggle={groupByVisibilityToggle}
|
|
||||||
scrollableContainerRef={scrollableContainerRef}
|
scrollableContainerRef={scrollableContainerRef}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
/>
|
/>
|
||||||
@ -197,6 +213,7 @@ export interface IKanBan {
|
|||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
|
subGroupIssueHeaderCount?: (listId: string) => number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||||
@ -221,6 +238,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
scrollableContainerRef,
|
scrollableContainerRef,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
|
subGroupIssueHeaderCount,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const issueKanBanView = useKanbanView();
|
const issueKanBanView = useKanbanView();
|
||||||
@ -248,6 +266,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
scrollableContainerRef={scrollableContainerRef}
|
scrollableContainerRef={scrollableContainerRef}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
|
subGroupIssueHeaderCount={subGroupIssueHeaderCount}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@ interface IKanbanGroup {
|
|||||||
viewId?: string;
|
viewId?: string;
|
||||||
disableIssueCreation?: boolean;
|
disableIssueCreation?: boolean;
|
||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
groupByVisibilityToggle: boolean;
|
groupByVisibilityToggle?: boolean;
|
||||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ interface ISubGroupSwimlaneHeader {
|
|||||||
list: IGroupByColumn[];
|
list: IGroupByColumn[];
|
||||||
kanbanFilters: TIssueKanbanFilters;
|
kanbanFilters: TIssueKanbanFilters;
|
||||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||||
|
showEmptyGroup: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSubGroupHeaderIssuesCount = (issueIds: TSubGroupedIssues, groupById: string) => {
|
const getSubGroupHeaderIssuesCount = (issueIds: TSubGroupedIssues, groupById: string) => {
|
||||||
@ -39,6 +40,22 @@ const getSubGroupHeaderIssuesCount = (issueIds: TSubGroupedIssues, groupById: st
|
|||||||
return headerCount;
|
return headerCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const visibilitySubGroupByGroupCount = (
|
||||||
|
issueIds: TSubGroupedIssues,
|
||||||
|
_list: IGroupByColumn,
|
||||||
|
showEmptyGroup: boolean
|
||||||
|
): boolean => {
|
||||||
|
let subGroupHeaderVisibility = true;
|
||||||
|
|
||||||
|
if (showEmptyGroup) subGroupHeaderVisibility = true;
|
||||||
|
else {
|
||||||
|
if (getSubGroupHeaderIssuesCount(issueIds, _list.id) > 0) subGroupHeaderVisibility = true;
|
||||||
|
else subGroupHeaderVisibility = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subGroupHeaderVisibility;
|
||||||
|
};
|
||||||
|
|
||||||
const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
||||||
issueIds,
|
issueIds,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
@ -46,25 +63,36 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
|||||||
list,
|
list,
|
||||||
kanbanFilters,
|
kanbanFilters,
|
||||||
handleKanbanFilters,
|
handleKanbanFilters,
|
||||||
|
showEmptyGroup,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="relative flex gap-2 h-max min-h-full w-full items-center">
|
<div className="relative flex gap-2 h-max min-h-full w-full items-center">
|
||||||
{list &&
|
{list &&
|
||||||
list.length > 0 &&
|
list.length > 0 &&
|
||||||
list.map((_list: IGroupByColumn) => (
|
list.map((_list: IGroupByColumn) => {
|
||||||
<div key={`${sub_group_by}_${_list.id}`} className="flex w-[350px] flex-shrink-0 flex-col">
|
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(
|
||||||
<HeaderGroupByCard
|
issueIds as TSubGroupedIssues,
|
||||||
sub_group_by={sub_group_by}
|
_list,
|
||||||
group_by={group_by}
|
showEmptyGroup
|
||||||
column_id={_list.id}
|
);
|
||||||
icon={_list.icon}
|
|
||||||
title={_list.name}
|
if (subGroupByVisibilityToggle === false) return <></>;
|
||||||
count={getSubGroupHeaderIssuesCount(issueIds as TSubGroupedIssues, _list?.id)}
|
|
||||||
kanbanFilters={kanbanFilters}
|
return (
|
||||||
handleKanbanFilters={handleKanbanFilters}
|
<div key={`${sub_group_by}_${_list.id}`} className="flex w-[350px] flex-shrink-0 flex-col">
|
||||||
issuePayload={_list.payload}
|
<HeaderGroupByCard
|
||||||
/>
|
sub_group_by={sub_group_by}
|
||||||
</div>
|
group_by={group_by}
|
||||||
))}
|
column_id={_list.id}
|
||||||
|
icon={_list.icon}
|
||||||
|
title={_list.name}
|
||||||
|
count={getSubGroupHeaderIssuesCount(issueIds as TSubGroupedIssues, _list?.id)}
|
||||||
|
kanbanFilters={kanbanFilters}
|
||||||
|
handleKanbanFilters={handleKanbanFilters}
|
||||||
|
issuePayload={_list.payload}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -124,52 +152,74 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
return issueCount;
|
return issueCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const visibilitySubGroupBy = (_list: IGroupByColumn): { showGroup: boolean; showIssues: boolean } => {
|
||||||
|
const subGroupVisibility = {
|
||||||
|
showGroup: true,
|
||||||
|
showIssues: true,
|
||||||
|
};
|
||||||
|
if (showEmptyGroup) subGroupVisibility.showGroup = true;
|
||||||
|
else {
|
||||||
|
if (calculateIssueCount(_list.id) > 0) subGroupVisibility.showGroup = true;
|
||||||
|
else subGroupVisibility.showGroup = false;
|
||||||
|
}
|
||||||
|
if (kanbanFilters?.sub_group_by.includes(_list.id)) subGroupVisibility.showIssues = false;
|
||||||
|
return subGroupVisibility;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-max min-h-full w-full">
|
<div className="relative h-max min-h-full w-full">
|
||||||
{list &&
|
{list &&
|
||||||
list.length > 0 &&
|
list.length > 0 &&
|
||||||
list.map((_list: any) => (
|
list.map((_list: any) => {
|
||||||
<div className="flex flex-shrink-0 flex-col">
|
const subGroupByVisibilityToggle = visibilitySubGroupBy(_list);
|
||||||
<div className="sticky top-[50px] z-[1] flex w-full items-center bg-custom-background-90 py-1">
|
if (subGroupByVisibilityToggle.showGroup === false) return <></>;
|
||||||
<div className="sticky left-0 flex-shrink-0 bg-custom-background-90 pr-2">
|
|
||||||
<HeaderSubGroupByCard
|
|
||||||
column_id={_list.id}
|
|
||||||
icon={_list.Icon}
|
|
||||||
title={_list.name || ""}
|
|
||||||
count={calculateIssueCount(_list.id)}
|
|
||||||
kanbanFilters={kanbanFilters}
|
|
||||||
handleKanbanFilters={handleKanbanFilters}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-full border-b border-dashed border-custom-border-400" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!kanbanFilters?.sub_group_by.includes(_list.id) && (
|
return (
|
||||||
<div className="relative">
|
<div className="flex flex-shrink-0 flex-col">
|
||||||
<KanBan
|
<div className="sticky top-[50px] z-[1] flex w-full items-center bg-custom-background-90 py-1">
|
||||||
issuesMap={issuesMap}
|
<div className="sticky left-0 flex-shrink-0 bg-custom-background-90 pr-2">
|
||||||
issueIds={(issueIds as TSubGroupedIssues)?.[_list.id]}
|
<HeaderSubGroupByCard
|
||||||
displayProperties={displayProperties}
|
column_id={_list.id}
|
||||||
sub_group_by={sub_group_by}
|
icon={_list.Icon}
|
||||||
group_by={group_by}
|
title={_list.name || ""}
|
||||||
sub_group_id={_list.id}
|
count={calculateIssueCount(_list.id)}
|
||||||
handleIssues={handleIssues}
|
kanbanFilters={kanbanFilters}
|
||||||
quickActions={quickActions}
|
handleKanbanFilters={handleKanbanFilters}
|
||||||
kanbanFilters={kanbanFilters}
|
/>
|
||||||
handleKanbanFilters={handleKanbanFilters}
|
</div>
|
||||||
showEmptyGroup={showEmptyGroup}
|
<div className="w-full border-b border-dashed border-custom-border-400" />
|
||||||
enableQuickIssueCreate={enableQuickIssueCreate}
|
|
||||||
canEditProperties={canEditProperties}
|
|
||||||
addIssuesToView={addIssuesToView}
|
|
||||||
quickAddCallback={quickAddCallback}
|
|
||||||
viewId={viewId}
|
|
||||||
scrollableContainerRef={scrollableContainerRef}
|
|
||||||
isDragStarted={isDragStarted}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
{subGroupByVisibilityToggle.showIssues && (
|
||||||
))}
|
<div className="relative">
|
||||||
|
<KanBan
|
||||||
|
issuesMap={issuesMap}
|
||||||
|
issueIds={(issueIds as TSubGroupedIssues)?.[_list.id]}
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
sub_group_by={sub_group_by}
|
||||||
|
group_by={group_by}
|
||||||
|
sub_group_id={_list.id}
|
||||||
|
handleIssues={handleIssues}
|
||||||
|
quickActions={quickActions}
|
||||||
|
kanbanFilters={kanbanFilters}
|
||||||
|
handleKanbanFilters={handleKanbanFilters}
|
||||||
|
showEmptyGroup={showEmptyGroup}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
canEditProperties={canEditProperties}
|
||||||
|
addIssuesToView={addIssuesToView}
|
||||||
|
quickAddCallback={quickAddCallback}
|
||||||
|
viewId={viewId}
|
||||||
|
scrollableContainerRef={scrollableContainerRef}
|
||||||
|
isDragStarted={isDragStarted}
|
||||||
|
subGroupIssueHeaderCount={(groupByListId: string) =>
|
||||||
|
getSubGroupHeaderIssuesCount(issueIds as TSubGroupedIssues, groupByListId)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -261,6 +311,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
kanbanFilters={kanbanFilters}
|
kanbanFilters={kanbanFilters}
|
||||||
handleKanbanFilters={handleKanbanFilters}
|
handleKanbanFilters={handleKanbanFilters}
|
||||||
list={groupByList}
|
list={groupByList}
|
||||||
|
showEmptyGroup={showEmptyGroup}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user