Compare commits

...

1 Commits

Author SHA1 Message Date
rahulramesha
28c9245fe1 modify changes to test parallel group calls 2024-07-29 15:23:37 +05:30
13 changed files with 184 additions and 72 deletions

View File

@@ -222,7 +222,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const kanbanFilters = issuesFilter?.issueFilters?.kanbanFilters || { group_by: [], sub_group_by: [] };
return (
<IssueLayoutHOC layout={EIssueLayoutTypes.KANBAN}>
<>
<DeleteIssueModal
dataId={draggedIssueId}
isOpen={deleteIssueModal}
@@ -279,6 +279,6 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
</div>
</div>
</div>
</IssueLayoutHOC>
</>
);
});

View File

@@ -156,7 +156,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
column_id={subList.id}
icon={subList.icon}
title={subList.name}
count={getGroupIssueCount(subList.id, undefined, false) ?? 0}
count={getGroupIssueCount(subList.id, undefined, false)}
issuePayload={subList.payload}
disableIssueCreation={disableIssueCreation || isGroupByCreatedBy}
addIssuesToView={addIssuesToView}

View File

@@ -15,6 +15,7 @@ import { CreateUpdateIssueModal } from "@/components/issues";
// hooks
import { useEventTracker } from "@/hooks/store";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import isNil from "lodash/isNil";
// types
interface IHeaderGroupByCard {
@@ -23,7 +24,7 @@ interface IHeaderGroupByCard {
column_id: string;
icon?: React.ReactNode;
title: string;
count: number;
count: number | undefined;
kanbanFilters: TIssueKanbanFilters;
handleKanbanFilters: any;
issuePayload: Partial<TIssue>;
@@ -123,11 +124,13 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
>
{title}
</div>
<div
className={`flex-shrink-0 text-sm font-medium text-custom-text-300 ${verticalAlignPosition ? `` : `pl-2`}`}
>
{count || 0}
</div>
{!isNil(count) && (
<div
className={`flex-shrink-0 text-sm font-medium text-custom-text-300 ${verticalAlignPosition ? `` : `pl-2`}`}
>
{count || 0}
</div>
)}
</div>
{sub_group_by === null && (

View File

@@ -28,11 +28,12 @@ import { GroupDragOverlay } from "../group-drag-overlay";
import { TRenderQuickActions } from "../list/list-view-types";
import { GroupDropLocation, getSourceFromDropPayload, getDestinationFromDropPayload, getIssueBlockId } from "../utils";
import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
import isNil from "lodash/isNil";
interface IKanbanGroup {
groupId: string;
issuesMap: IIssueMap;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
displayProperties: IIssueDisplayProperties | undefined;
sub_group_by: TIssueGroupByOptions | undefined;
group_by: TIssueGroupByOptions | undefined;
@@ -88,8 +89,8 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
const containerRef = sub_group_by && scrollableContainerRef ? scrollableContainerRef : columnRef;
const loadMoreIssuesInThisGroup = useCallback(() => {
loadMoreIssues(groupId, sub_group_id === "null"? undefined: sub_group_id)
}, [loadMoreIssues, groupId, sub_group_id])
loadMoreIssues(groupId, sub_group_id === "null" ? undefined : sub_group_id);
}, [loadMoreIssues, groupId, sub_group_id]);
const isPaginating = !!getIssueLoader(groupId, sub_group_id);
@@ -218,11 +219,10 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
? (groupedIssueIds as TSubGroupedIssues)?.[groupId]?.[sub_group_id] ?? []
: (groupedIssueIds as TGroupedIssues)?.[groupId] ?? [];
const groupIssueCount = getGroupIssueCount(groupId, sub_group_id, false) ?? 0;
const groupIssueCount = getGroupIssueCount(groupId, sub_group_id, false);
const nextPageResults = getPaginationData(groupId, sub_group_id)?.nextPageResults;
const loadMore = isPaginating ? (
<KanbanIssueBlockLoader />
) : (
@@ -235,8 +235,9 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
</div>
);
const shouldLoadMore = nextPageResults === undefined ? issueIds?.length < groupIssueCount : !!nextPageResults;
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
const shouldLoadMore =
nextPageResults === undefined ? isNil(groupIssueCount) || issueIds?.length < groupIssueCount : !!nextPageResults;
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
return (
@@ -270,7 +271,7 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
canDropOverIssue={!canOverlayBeVisible}
/>
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
{shouldLoadMore && (isSubGroup ? <>{loadMore}</> : <KanbanIssueBlockLoader ref={setIntersectionElement} />)}
{enableQuickIssueCreate && !disableIssueCreation && (
<div className="w-full bg-custom-background-90 py-0.5 sticky bottom-0">

View File

@@ -56,9 +56,9 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = observer(
{list &&
list.length > 0 &&
list.map((_list: IGroupByColumn) => {
const groupCount = getGroupIssueCount(_list?.id, undefined, false) ?? 0;
const groupCount = getGroupIssueCount(_list?.id, undefined, false);
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount, showEmptyGroup);
const subGroupByVisibilityToggle = visibilitySubGroupByGroupCount(groupCount ?? 0, showEmptyGroup);
if (subGroupByVisibilityToggle === false) return <></>;

View File

@@ -107,27 +107,25 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
);
return (
<IssueLayoutHOC layout={EIssueLayoutTypes.LIST}>
<div className={`relative size-full bg-custom-background-90`}>
<List
issuesMap={issueMap}
displayProperties={displayProperties}
group_by={group_by}
orderBy={orderBy}
updateIssue={updateIssue}
quickActions={renderQuickActions}
groupedIssueIds={groupedIssueIds ?? {}}
loadMoreIssues={loadMoreIssues}
showEmptyGroup={showEmptyGroup}
quickAddCallback={quickAddIssue}
enableIssueQuickAdd={!!enableQuickAdd}
canEditProperties={canEditProperties}
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
addIssuesToView={addIssuesToView}
isCompletedCycle={isCompletedCycle}
handleOnDrop={handleOnDrop}
/>
</div>
</IssueLayoutHOC>
<div className={`relative size-full bg-custom-background-90`}>
<List
issuesMap={issueMap}
displayProperties={displayProperties}
group_by={group_by}
orderBy={orderBy}
updateIssue={updateIssue}
quickActions={renderQuickActions}
groupedIssueIds={groupedIssueIds ?? {}}
loadMoreIssues={loadMoreIssues}
showEmptyGroup={showEmptyGroup}
quickAddCallback={quickAddIssue}
enableIssueQuickAdd={!!enableQuickAdd}
canEditProperties={canEditProperties}
disableIssueCreation={!enableIssueCreation || !isEditingAllowed}
addIssuesToView={addIssuesToView}
isCompletedCycle={isCompletedCycle}
handleOnDrop={handleOnDrop}
/>
</div>
);
});

View File

@@ -18,12 +18,13 @@ import { cn } from "@/helpers/common.helper";
import { useEventTracker } from "@/hooks/store";
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
import { TSelectionHelper } from "@/hooks/use-multiple-select";
import isNil from "lodash/isNil";
interface IHeaderGroupByCard {
groupID: string;
icon?: React.ReactNode;
title: string;
count: number;
count: number | undefined;
issuePayload: Partial<TIssue>;
canEditProperties: (projectId: string | undefined) => boolean;
toggleListGroup: () => void;
@@ -98,7 +99,7 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
)}
groupID={groupID}
selectionHelpers={selectionHelpers}
disabled={count === 0}
disabled={!count}
/>
</div>
)}
@@ -106,10 +107,12 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
{icon ?? <CircleDashed className="size-3.5" strokeWidth={2} />}
</div>
<div className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
onClick={toggleListGroup}>
<div
className="relative flex w-full flex-row items-center gap-1 overflow-hidden cursor-pointer"
onClick={toggleListGroup}
>
<div className="inline-block line-clamp-1 truncate font-medium text-custom-text-100">{title}</div>
<div className="pl-2 text-sm font-medium text-custom-text-300">{count || 0}</div>
{!isNil(count) && <div className="pl-2 text-sm font-medium text-custom-text-300">{count || 0}</div>}
</div>
{!disableIssueCreation &&

View File

@@ -37,6 +37,7 @@ import { IssueBlocksList } from "./blocks-list";
import { HeaderGroupByCard } from "./headers/group-by-card";
import { TRenderQuickActions } from "./list-view-types";
import { ListQuickAddIssueForm } from "./quick-add-issue-form";
import isNil from "lodash/isNil";
interface Props {
groupIssueIds: string[] | undefined;
@@ -98,7 +99,7 @@ export const ListGroup = observer((props: Props) => {
const [intersectionElement, setIntersectionElement] = useState<HTMLDivElement | null>(null);
const groupIssueCount = getGroupIssueCount(group.id, undefined, false) ?? 0;
const groupIssueCount = getGroupIssueCount(group.id, undefined, false);
const nextPageResults = getPaginationData(group.id, undefined)?.nextPageResults;
const isPaginating = !!getIssueLoader(group.id);
@@ -106,7 +107,7 @@ export const ListGroup = observer((props: Props) => {
const shouldLoadMore =
nextPageResults === undefined && groupIssueCount !== undefined && groupIssueIds
? groupIssueIds.length < groupIssueCount
? isNil(groupIssueCount) || groupIssueIds.length < groupIssueCount
: !!nextPageResults;
const loadMore = isPaginating ? (

View File

@@ -39,6 +39,7 @@ import { IMemberRootStore } from "@/store/member";
import { IModuleStore } from "@/store/module.store";
import { IProjectStore } from "@/store/project/project.store";
import { IStateStore } from "@/store/state.store";
import { ALL_ISSUES } from "@plane/constants";
export const HIGHLIGHT_CLASS = "highlight";
export const HIGHLIGHT_WITH_LINE = "highlight-with-line";
@@ -92,8 +93,10 @@ export const getGroupByColumns = (
case "created_by":
return getCreatedByColumns(member) as any;
default:
if (includeNone) return [{ id: `All Issues`, name: `All Issues`, payload: {}, icon: undefined }];
if (includeNone) return [{ id: ALL_ISSUES, name: ALL_ISSUES, payload: {}, icon: undefined }];
}
return;
};
const getProjectColumns = (project: IProjectStore): IGroupByColumn[] | undefined => {

View File

@@ -59,37 +59,37 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null
);
// fetching project labels
useSWR(
const { isLoading: isLabelsLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_LABELS_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
// fetching project members
useSWR(
const { isLoading: isMembersLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_MEMBERS_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
// fetching project states
useSWR(
const { isLoading: isStateLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
// fetching project estimates
useSWR(
const { isLoading: isEstimatesLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => getProjectEstimates(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
// fetching project cycles
useSWR(
const { isLoading: isCyclesLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchAllCycles(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
);
// fetching project modules
useSWR(
const { isLoading: isModulesLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_MODULES_${workspaceSlug}_${projectId}` : null,
workspaceSlug && projectId ? () => fetchModules(workspaceSlug.toString(), projectId.toString()) : null,
{ revalidateIfStale: false, revalidateOnFocus: false }
@@ -102,8 +102,11 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
);
const projectExists = projectId ? getProjectById(projectId.toString()) : null;
const isLoading =
isLabelsLoading || isMembersLoading || isStateLoading || isEstimatesLoading || isCyclesLoading || isModulesLoading;
// check if the project member apis is loading
if (!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null)
if ((!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null) || isLoading)
return (
<div className="grid h-screen place-items-center bg-custom-background-100 p-4">
<div className="flex flex-col items-center gap-3 text-center">

View File

@@ -44,6 +44,7 @@ import {
getSubGroupIssueKeyActions,
} from "./base-issues-utils";
import { IBaseIssueFilterStore } from "./issue-filter-helper.store";
import { getGroupByColumns } from "@/components/issues/issue-layouts/utils";
// constants
// helpers
// services
@@ -419,6 +420,56 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
}
);
getGroups = (isWorkspaceLevel = false) => {
if (!this.groupBy || this.groupBy === "target_date") return;
const {
projectRoot,
cycle,
module: moduleInfo,
label,
state: projectState,
memberRoot,
} = this.rootIssueStore.rootStore;
return getGroupByColumns(
this.groupBy,
projectRoot.project,
cycle,
moduleInfo,
label,
projectState,
memberRoot,
false,
isWorkspaceLevel
);
};
getSubGroups = (isWorkspaceLevel = false) => {
if (!this.subGroupBy || this.subGroupBy === "target_date") return;
const {
projectRoot,
cycle,
module: moduleInfo,
label,
state: projectState,
memberRoot,
} = this.rootIssueStore.rootStore;
return getGroupByColumns(
this.subGroupBy,
projectRoot.project,
cycle,
moduleInfo,
label,
projectState,
memberRoot,
false,
isWorkspaceLevel
);
};
/**
* Gets the next page cursor based on number of issues currently available
* @param groupId groupId for the cursor
@@ -451,9 +502,8 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
onfetchIssues(
issuesResponse: TIssuesResponse,
options: IssuePaginationOptions,
workspaceSlug: string,
projectId?: string,
id?: string
groupId?: string,
subGroupId?: string
) {
// Process the Issue Response to get the following data from it
const { issueList, groupedIssues, groupedIssueCount } = this.processIssueResponse(issuesResponse);
@@ -463,15 +513,12 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
// Update all the GroupIds to this Store's groupedIssueIds and update Individual group issue counts
runInAction(() => {
this.updateGroupedIssueIds(groupedIssues, groupedIssueCount);
this.loader[getGroupKey()] = undefined;
this.updateGroupedIssueIds(groupedIssues, groupedIssueCount, groupId, subGroupId);
this.loader[getGroupKey(groupId, subGroupId)] = undefined;
});
// fetch parent stats if required, to be handled in the Implemented class
this.fetchParentStats(workspaceSlug, projectId, id);
// store Pagination options for next subsequent calls and data like next cursor etc
this.storePreviousPaginationValues(issuesResponse, options);
this.storePreviousPaginationValues(issuesResponse, options, groupId, subGroupId);
}
/**

View File

@@ -282,7 +282,7 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
subGroupId?: string
) {
// if cursor exists, use the cursor. If it doesn't exist construct the cursor based on per page count
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:1:0` : `${options.perPageCount}:0:0`;
const pageCursor = cursor ? cursor : groupId ? `${options.perPageCount}:0:0` : `${options.perPageCount}:0:0`;
// pagination params
const paginationParams: Partial<Record<TIssueParams, string | boolean>> = {

View File

@@ -86,21 +86,74 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
isExistingPaginationOptions: boolean = false
) => {
try {
// set loader and clear store
runInAction(() => {
this.setLoader(loadType);
});
const groups = this.getGroups();
const subGroups = this.getSubGroups();
this.clear(!isExistingPaginationOptions);
if (!groups || (!groups && !subGroups))
return await this.fetchParallelIssues(workspaceSlug, projectId, loadType, options);
const promises = [];
for (const group of groups) {
if (!subGroups) {
promises.push(this.fetchParallelIssues(workspaceSlug, projectId, loadType, options, group.id));
continue;
}
for (const subGroup of subGroups) {
promises.push(this.fetchParallelIssues(workspaceSlug, projectId, loadType, options, group.id, subGroup.id));
}
}
await Promise.all(promises);
// fetch parent stats if required, to be handled in the Implemented class
this.fetchParentStats(workspaceSlug, projectId);
} catch (error) {
// set loader to undefined if errored out
this.setLoader(undefined);
throw error;
}
};
/**
* This method is called to fetch the first issues of pagination
* @param workspaceSlug
* @param projectId
* @param loadType
* @param options
* @returns
*/
fetchParallelIssues = async (
workspaceSlug: string,
projectId: string,
loadType: TLoader = "init-loader",
options: IssuePaginationOptions,
groupId?: string,
subGroupId?: string
) => {
try {
// set loader and clear store
runInAction(() => {
// set Loader
if (groupId || subGroupId) {
this.setLoader("pagination", groupId, subGroupId);
} else {
this.setLoader(loadType);
}
});
// get params from pagination options
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, undefined, undefined);
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, groupId, subGroupId);
// call the fetch issues API with the params
const response = await this.issueService.getIssues(workspaceSlug, projectId, params, {
signal: this.controller.signal,
});
// after fetching issues, call the base method to process the response further
this.onfetchIssues(response, options, workspaceSlug, projectId);
this.onfetchIssues(response, options, groupId, subGroupId);
return response;
} catch (error) {
// set loader to undefined if errored out