mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
Compare commits
1 Commits
feat-date-
...
test-indiv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48acacfc64 |
@@ -26,6 +26,7 @@ import { IQuickActionProps, TRenderQuickActions } from "../list/list-view-types"
|
||||
import { getSourceFromDropPayload } from "../utils";
|
||||
import { KanBan } from "./default";
|
||||
import { KanBanSwimLanes } from "./swimlanes";
|
||||
import { IssuePaginationOptions } from "@plane/types";
|
||||
|
||||
export type KanbanStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
@@ -53,7 +54,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const { issueMap, issuesFilter, issues } = useIssues(storeType);
|
||||
const { issueMap, issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT);
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
} = useIssueDetail();
|
||||
@@ -67,7 +68,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
archiveIssue,
|
||||
restoreIssue,
|
||||
updateFilters,
|
||||
} = useIssuesActions(storeType);
|
||||
} = useIssuesActions(EIssuesStoreType.PROJECT);
|
||||
|
||||
const deleteAreaRef = useRef<HTMLDivElement | null>(null);
|
||||
const [isDragOverDelete, setIsDragOverDelete] = useState(false);
|
||||
@@ -82,24 +83,21 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
|
||||
const orderBy = displayFilters?.order_by;
|
||||
|
||||
useEffect(() => {
|
||||
fetchIssues("init-loader", { canGroup: true, perPageCount: sub_group_by ? 10 : 30 }, viewId);
|
||||
}, [fetchIssues, storeType, group_by, sub_group_by, viewId]);
|
||||
// useEffect(() => {
|
||||
// fetchIssues("init-loader", { canGroup: true, perPageCount: sub_group_by ? 10 : 30 }, viewId);
|
||||
// }, [fetchIssues, storeType, group_by, sub_group_by, viewId]);
|
||||
|
||||
const fetchMoreIssues = useCallback(
|
||||
(groupId?: string, subgroupId?: string) => {
|
||||
(groupId?: string, subgroupId?: string, options?: IssuePaginationOptions) => {
|
||||
if (issues?.getIssueLoader(groupId, subgroupId) !== "pagination") {
|
||||
fetchNextIssues(groupId, subgroupId);
|
||||
fetchNextIssues(groupId, subgroupId, options);
|
||||
}
|
||||
},
|
||||
[fetchNextIssues]
|
||||
);
|
||||
|
||||
const debouncedFetchMoreIssues = debounce(
|
||||
(groupId?: string, subgroupId?: string) => fetchMoreIssues(groupId, subgroupId),
|
||||
300,
|
||||
{ leading: true, trailing: false }
|
||||
);
|
||||
const debouncedFetchMoreIssues = (groupId?: string, subgroupId?: string, options?: IssuePaginationOptions) =>
|
||||
fetchMoreIssues(groupId, subgroupId, options);
|
||||
|
||||
const groupedIssueIds = issues?.groupedIssueIds;
|
||||
|
||||
@@ -222,7 +220,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 +277,6 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IssueLayoutHOC>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
TIssueGroupByOptions,
|
||||
TIssueOrderByOptions,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
// hooks
|
||||
@@ -43,7 +44,7 @@ export interface IKanBan {
|
||||
quickActions: TRenderQuickActions;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: any;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
|
||||
disableIssueCreation?: boolean;
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
TSubGroupedIssues,
|
||||
TIssueGroupByOptions,
|
||||
TIssueOrderByOptions,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { highlightIssueOnDrop } from "@/components/issues/issue-layouts/utils";
|
||||
@@ -32,7 +33,7 @@ import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
|
||||
interface IKanbanGroup {
|
||||
groupId: string;
|
||||
issuesMap: IIssueMap;
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
sub_group_by: TIssueGroupByOptions | undefined;
|
||||
group_by: TIssueGroupByOptions | undefined;
|
||||
@@ -44,7 +45,7 @@ groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
quickActions: TRenderQuickActions;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
|
||||
disableIssueCreation?: boolean;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
groupByVisibilityToggle?: boolean;
|
||||
@@ -88,9 +89,15 @@ 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, sub_group_by]);
|
||||
|
||||
useEffect(() => {
|
||||
loadMoreIssues(groupId, sub_group_id === "null" ? undefined : sub_group_id, {
|
||||
canGroup: true,
|
||||
perPageCount: sub_group_by ? 10 : 30,
|
||||
});
|
||||
}, [loadMoreIssues, groupId, sub_group_id, sub_group_by]);
|
||||
const isPaginating = !!getIssueLoader(groupId, sub_group_id);
|
||||
|
||||
useIntersectionObserver(
|
||||
@@ -222,7 +229,6 @@ export const KanbanGroup = observer((props: IKanbanGroup) => {
|
||||
|
||||
const nextPageResults = getPaginationData(groupId, sub_group_id)?.nextPageResults;
|
||||
|
||||
|
||||
const loadMore = isPaginating ? (
|
||||
<KanbanIssueBlockLoader />
|
||||
) : (
|
||||
@@ -235,8 +241,13 @@ 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
|
||||
? groupIssueCount !== undefined
|
||||
? issueIds?.length < groupIssueCount
|
||||
: true
|
||||
: !!nextPageResults;
|
||||
const canOverlayBeVisible = orderBy !== "sort_order" || isDropDisabled;
|
||||
const shouldOverlayBeVisible = isDraggingOverColumn && canOverlayBeVisible;
|
||||
|
||||
return (
|
||||
@@ -270,7 +281,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">
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
TIssueGroupByOptions,
|
||||
TIssueOrderByOptions,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
// hooks
|
||||
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store";
|
||||
@@ -104,7 +105,7 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
|
||||
}
|
||||
|
||||
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||
@@ -223,7 +224,7 @@ export interface IKanBanSwimLanes {
|
||||
quickActions: TRenderQuickActions;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => void;
|
||||
showEmptyGroup: boolean;
|
||||
handleOnDrop: (source: GroupDropLocation, destination: GroupDropLocation) => Promise<void>;
|
||||
disableIssueCreation?: boolean;
|
||||
|
||||
@@ -21,7 +21,11 @@ interface IssueActions {
|
||||
options: IssuePaginationOptions,
|
||||
viewId?: string
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (groupId?: string, subGroupId?: string) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (
|
||||
groupId?: string,
|
||||
subGroupId?: string,
|
||||
options?: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
removeIssue: (projectId: string | undefined | null, issueId: string) => Promise<void>;
|
||||
createIssue?: (projectId: string | undefined | null, data: Partial<TIssue>) => Promise<TIssue | undefined>;
|
||||
quickAddIssue?: (projectId: string | undefined | null, data: TIssue) => Promise<TIssue | undefined>;
|
||||
@@ -76,16 +80,16 @@ const useProjectIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
async (loadType: TLoader, options: IssuePaginationOptions, groupId?: string, subGroupId?: string) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(
|
||||
async (groupId?: string, subGroupId?: string) => {
|
||||
async (groupId?: string, subGroupId?: string, options?: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), groupId, subGroupId);
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), groupId, subGroupId, options);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
@@ -482,7 +482,12 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
* @param groupId
|
||||
* @param subGroupId
|
||||
*/
|
||||
onfetchNexIssues(issuesResponse: TIssuesResponse, groupId?: string, subGroupId?: string) {
|
||||
onfetchNexIssues(
|
||||
issuesResponse: TIssuesResponse,
|
||||
groupId?: string,
|
||||
subGroupId?: string,
|
||||
options?: IssuePaginationOptions
|
||||
) {
|
||||
// Process the Issue Response to get the following data from it
|
||||
const { issueList, groupedIssues, groupedIssueCount } = this.processIssueResponse(issuesResponse);
|
||||
|
||||
@@ -496,7 +501,7 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||
});
|
||||
|
||||
// store Pagination data like next cursor etc
|
||||
this.storePreviousPaginationValues(issuesResponse, undefined, groupId, subGroupId);
|
||||
this.storePreviousPaginationValues(issuesResponse, options, groupId, subGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>> = {
|
||||
|
||||
@@ -15,7 +15,9 @@ export interface IProjectIssues extends IBaseIssuesStore {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
option: IssuePaginationOptions
|
||||
option: IssuePaginationOptions,
|
||||
groupId?: string,
|
||||
subGroupId?: string
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
@@ -26,7 +28,8 @@ export interface IProjectIssues extends IBaseIssuesStore {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
groupId?: string,
|
||||
subGroupId?: string
|
||||
subGroupId?: string,
|
||||
options?: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
@@ -83,6 +86,8 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
options: IssuePaginationOptions,
|
||||
groupId?: string,
|
||||
subGroupId?: string,
|
||||
isExistingPaginationOptions: boolean = false
|
||||
) => {
|
||||
try {
|
||||
@@ -93,14 +98,14 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
this.clear(!isExistingPaginationOptions);
|
||||
|
||||
// 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.onfetchNexIssues(response, groupId, subGroupId, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
// set loader to undefined if errored out
|
||||
@@ -119,17 +124,23 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
* @param subGroupId
|
||||
* @returns
|
||||
*/
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string, groupId?: string, subGroupId?: string) => {
|
||||
fetchNextIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
groupId?: string,
|
||||
subGroupId?: string,
|
||||
options?: IssuePaginationOptions
|
||||
) => {
|
||||
const cursorObject = this.getPaginationData(groupId, subGroupId);
|
||||
// if there are no pagination options and the next page results do not exist the return
|
||||
if (!this.paginationOptions || (cursorObject && !cursorObject?.nextPageResults)) return;
|
||||
if (!this.paginationOptions && !options) return;
|
||||
try {
|
||||
// set Loader
|
||||
this.setLoader("pagination", groupId, subGroupId);
|
||||
|
||||
// get params from stored pagination options
|
||||
const params = this.issueFilterStore?.getFilterParams(
|
||||
this.paginationOptions,
|
||||
options ?? this.paginationOptions!,
|
||||
projectId,
|
||||
this.getNextCursor(groupId, subGroupId),
|
||||
groupId,
|
||||
@@ -139,7 +150,7 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
// after the next page of issues are fetched, call the base method to process the response
|
||||
this.onfetchNexIssues(response, groupId, subGroupId);
|
||||
this.onfetchNexIssues(response, groupId, subGroupId, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
// set Loader as undefined if errored out
|
||||
@@ -162,7 +173,15 @@ export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
loadType: TLoader = "mutation"
|
||||
) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions, true);
|
||||
return await this.fetchIssues(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
loadType,
|
||||
this.paginationOptions,
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
// Using aliased names as they cannot be overridden in other stores
|
||||
|
||||
Reference in New Issue
Block a user