mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
make store changes for pagination
This commit is contained in:
parent
425b36e391
commit
026bc9318f
14
packages/types/src/issues/base.d.ts
vendored
14
packages/types/src/issues/base.d.ts
vendored
@ -1,3 +1,6 @@
|
||||
import { StateGroup } from "components/states";
|
||||
import { TIssuePriorities } from "../issues";
|
||||
|
||||
// issues
|
||||
export * from "./issue";
|
||||
export * from "./issue_reaction";
|
||||
@ -7,16 +10,17 @@ export * from "./issue_relation";
|
||||
export * from "./issue_sub_issues";
|
||||
export * from "./activity/base";
|
||||
|
||||
export type TLoader = "init-loader" | "mutation" | undefined;
|
||||
export type TLoader = "init-loader" | "mutation" | "pagination" | undefined;
|
||||
|
||||
export type TGroupedIssues = {
|
||||
[group_id: string]: string[];
|
||||
[group_id: string]: { issueIds: string[]; issueCount: number };
|
||||
};
|
||||
|
||||
export type TSubGroupedIssues = {
|
||||
[sub_grouped_id: string]: {
|
||||
[group_id: string]: string[];
|
||||
[sub_grouped_id: string]: TGroupedIssues;
|
||||
};
|
||||
export type TUnGroupedIssues = {
|
||||
"All Issues": { issueIds: string[]; issueCount: number };
|
||||
};
|
||||
|
||||
export type TUnGroupedIssues = string[];
|
||||
export type TIssues = TGroupedIssues | TUnGroupedIssues;
|
||||
|
38
packages/types/src/issues/issue.d.ts
vendored
38
packages/types/src/issues/issue.d.ts
vendored
@ -4,15 +4,15 @@ import { TIssueLink } from "./issue_link";
|
||||
import { TIssueReaction } from "./issue_reaction";
|
||||
|
||||
// new issue structure types
|
||||
export type TIssue = {
|
||||
|
||||
export type TBaseIssue = {
|
||||
id: string;
|
||||
sequence_id: number;
|
||||
name: string;
|
||||
description_html: string;
|
||||
sort_order: number;
|
||||
|
||||
state_id: string;
|
||||
priority: TIssuePriorities;
|
||||
state_id: string | null;
|
||||
priority: TIssuePriorities | null;
|
||||
label_ids: string[];
|
||||
assignee_ids: string[];
|
||||
estimate_point: number | null;
|
||||
@ -21,10 +21,10 @@ export type TIssue = {
|
||||
attachment_count: number;
|
||||
link_count: number;
|
||||
|
||||
project_id: string;
|
||||
project_id: string | null;
|
||||
parent_id: string | null;
|
||||
cycle_id: string | null;
|
||||
module_ids: string[] | null;
|
||||
module_ids: string[];
|
||||
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
@ -37,9 +37,14 @@ export type TIssue = {
|
||||
updated_by: string;
|
||||
|
||||
is_draft: boolean;
|
||||
};
|
||||
|
||||
export type TIssue = TBaseIssue & {
|
||||
description_html?: string;
|
||||
is_subscribed?: boolean;
|
||||
|
||||
parent?: partial<TIssue>;
|
||||
|
||||
issue_reactions?: TIssueReaction[];
|
||||
issue_attachment?: TIssueAttachment[];
|
||||
issue_link?: TIssueLink[];
|
||||
@ -51,3 +56,24 @@ export type TIssue = {
|
||||
export type TIssueMap = {
|
||||
[issue_id: string]: TIssue;
|
||||
};
|
||||
|
||||
type TIssueResponseResults =
|
||||
| TBaseIssue[]
|
||||
| {
|
||||
[key: string]: {
|
||||
results: TBaseIssue[];
|
||||
total_results: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type TIssuesResponse = {
|
||||
grouped_by: string;
|
||||
next_cursor: string;
|
||||
prev_cursor: string;
|
||||
next_page_results: boolean;
|
||||
prev_page_results: boolean;
|
||||
count: number;
|
||||
total_pages: number;
|
||||
extra_stats: null;
|
||||
results: TIssueResponseResults;
|
||||
};
|
||||
|
15
packages/types/src/view-props.d.ts
vendored
15
packages/types/src/view-props.d.ts
vendored
@ -13,7 +13,6 @@ export type TIssueGroupByOptions =
|
||||
| "state_detail.group"
|
||||
| "project"
|
||||
| "assignees"
|
||||
| "mentions"
|
||||
| "cycle"
|
||||
| "module"
|
||||
| null;
|
||||
@ -72,7 +71,9 @@ export type TIssueParams =
|
||||
| "order_by"
|
||||
| "type"
|
||||
| "sub_issue"
|
||||
| "show_empty_groups";
|
||||
| "show_empty_groups"
|
||||
| "cursor"
|
||||
| "per_page";
|
||||
|
||||
export type TCalendarLayouts = "month" | "week";
|
||||
|
||||
@ -82,9 +83,9 @@ export interface IIssueFilterOptions {
|
||||
created_by?: string[] | null;
|
||||
labels?: string[] | null;
|
||||
priority?: string[] | null;
|
||||
project?: string[] | null;
|
||||
cycle?: string[] | null;
|
||||
module?: string[] | null;
|
||||
project?: string[] | null;
|
||||
start_date?: string[] | null;
|
||||
state?: string[] | null;
|
||||
state_group?: string[] | null;
|
||||
@ -191,3 +192,11 @@ export interface IWorkspaceGlobalViewProps {
|
||||
display_filters: IWorkspaceIssueDisplayFilterOptions | undefined;
|
||||
display_properties: IIssueDisplayProperties;
|
||||
}
|
||||
|
||||
export interface IssuePaginationOptions {
|
||||
canGroup: boolean;
|
||||
perPageCount: number;
|
||||
greaterThanDate?: Date;
|
||||
lessThanDate?: Date;
|
||||
groupedBy?: TIssueGroupByOptions;
|
||||
}
|
||||
|
@ -427,3 +427,17 @@ export const groupReactionEmojis = (reactions: any) => {
|
||||
|
||||
return _groupedEmojis;
|
||||
};
|
||||
|
||||
|
||||
export enum IssueGroupByOptions {
|
||||
"state" = "state_id",
|
||||
"priority" = "priority",
|
||||
"labels" = "labels__id",
|
||||
"state_detail.group" = "state__group",
|
||||
"assignees" = "assignees__id",
|
||||
"cycle" = "cycle_id",
|
||||
"module" = "modules__id",
|
||||
"target_date" = "target_date",
|
||||
"project" = "project_id",
|
||||
"created_by" = "created_by",
|
||||
}
|
||||
|
@ -4,14 +4,21 @@ import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
IIssueFilterOptions,
|
||||
IssuePaginationOptions,
|
||||
TIssue,
|
||||
TIssueKanbanFilters,
|
||||
TIssuesResponse,
|
||||
TLoader,
|
||||
} from "@plane/types";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
interface IssueActions {
|
||||
fetchIssues?: (projectId: string, loadType: TLoader) => Promise<TIssue[] | undefined>;
|
||||
fetchIssues: (
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
userViewId?: "assigned" | "created" | "subscribed"
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: () => Promise<TIssuesResponse | undefined>;
|
||||
removeIssue: (projectId: string, issueId: string) => Promise<void>;
|
||||
createIssue?: (projectId: string, data: Partial<TIssue>) => Promise<TIssue | undefined>;
|
||||
updateIssue?: (projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
@ -29,25 +36,25 @@ export const useIssuesActions = (storeType: EIssuesStoreType): IssueActions => {
|
||||
const projectIssueActions = useProjectIssueActions();
|
||||
const cycleIssueActions = useCycleIssueActions();
|
||||
const moduleIssueActions = useModuleIssueActions();
|
||||
const profileIssueActions = useProfileIssueActions();
|
||||
const projectViewIssueActions = useProjectViewIssueActions();
|
||||
const globalIssueActions = useGlobalIssueActions();
|
||||
const profileIssueActions = useProfileIssueActions();
|
||||
const draftIssueActions = useDraftIssueActions();
|
||||
const archivedIssueActions = useArchivedIssueActions();
|
||||
const globalIssueActions = useGlobalIssueActions();
|
||||
|
||||
switch (storeType) {
|
||||
case EIssuesStoreType.PROJECT_VIEW:
|
||||
return projectViewIssueActions;
|
||||
case EIssuesStoreType.PROFILE:
|
||||
return profileIssueActions;
|
||||
case EIssuesStoreType.CYCLE:
|
||||
return cycleIssueActions;
|
||||
case EIssuesStoreType.MODULE:
|
||||
return moduleIssueActions;
|
||||
case EIssuesStoreType.ARCHIVED:
|
||||
return archivedIssueActions;
|
||||
case EIssuesStoreType.DRAFT:
|
||||
return draftIssueActions;
|
||||
case EIssuesStoreType.CYCLE:
|
||||
return cycleIssueActions;
|
||||
case EIssuesStoreType.MODULE:
|
||||
return moduleIssueActions;
|
||||
case EIssuesStoreType.GLOBAL:
|
||||
return globalIssueActions;
|
||||
case EIssuesStoreType.PROJECT:
|
||||
@ -60,16 +67,21 @@ const useProjectIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
router: { workspaceSlug, projectId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
@ -114,13 +126,14 @@ const useProjectIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
[fetchIssues, fetchNextIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
@ -128,16 +141,21 @@ const useCycleIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, cycleId },
|
||||
router: { workspaceSlug, projectId, cycleId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, cycleId);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options, cycleId.toString());
|
||||
},
|
||||
[issues.fetchIssues, cycleId, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, projectId, cycleId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), cycleId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId, cycleId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
@ -147,17 +165,17 @@ const useCycleIssueActions = () => {
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, cycleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, cycleId, workspaceSlug]
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, cycleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, cycleId, workspaceSlug]
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssueFromView = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
@ -168,10 +186,10 @@ const useCycleIssueActions = () => {
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, cycleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.archiveIssue, cycleId, workspaceSlug]
|
||||
[issues.archiveIssue, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
@ -189,6 +207,7 @@ const useCycleIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
@ -196,7 +215,16 @@ const useCycleIssueActions = () => {
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, removeIssueFromView, archiveIssue, updateFilters]
|
||||
[
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
removeIssueFromView,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
@ -204,16 +232,21 @@ const useModuleIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, moduleId },
|
||||
router: { workspaceSlug, projectId, moduleId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, moduleId);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options, moduleId.toString());
|
||||
},
|
||||
[issues.fetchIssues, moduleId, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, projectId, moduleId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), moduleId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId, moduleId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
@ -223,17 +256,17 @@ const useModuleIssueActions = () => {
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, moduleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, moduleId, workspaceSlug]
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, moduleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, moduleId, workspaceSlug]
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssueFromView = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
@ -244,8 +277,8 @@ const useModuleIssueActions = () => {
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, moduleId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.archiveIssue, moduleId, workspaceSlug]
|
||||
);
|
||||
@ -265,6 +298,7 @@ const useModuleIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
@ -284,39 +318,44 @@ const useProfileIssueActions = () => {
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, userId);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions, viewId?: "assigned" | "created" | "subscribed") => {
|
||||
if (!workspaceSlug || !userId || !viewId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), userId.toString(), loadType, options, viewId);
|
||||
},
|
||||
[issues.fetchIssues, userId, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, userId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), userId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, userId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, userId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
||||
},
|
||||
[issues.createIssue, userId, workspaceSlug]
|
||||
[issues.createIssue, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, userId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, userId, workspaceSlug]
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, userId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, userId, workspaceSlug]
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, userId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.archiveIssue, userId, workspaceSlug]
|
||||
[issues.archiveIssue, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
@ -334,6 +373,7 @@ const useProfileIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
@ -348,43 +388,48 @@ const useProjectViewIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, viewId },
|
||||
router: { workspaceSlug, projectId, viewId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, viewId);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues, viewId, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, viewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
||||
},
|
||||
[issues.createIssue, viewId, workspaceSlug]
|
||||
[issues.createIssue, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, viewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, viewId, workspaceSlug]
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, viewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, viewId, workspaceSlug]
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, viewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.archiveIssue, viewId, workspaceSlug]
|
||||
[issues.archiveIssue, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
@ -402,13 +447,14 @@ const useProjectViewIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
[fetchIssues, fetchNextIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
@ -416,16 +462,21 @@ const useDraftIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
router: { workspaceSlug, projectId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug]
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
@ -463,6 +514,7 @@ const useDraftIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
@ -476,16 +528,21 @@ const useArchivedIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
router: { workspaceSlug, projectId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues]
|
||||
[issues.fetchIssues, workspaceSlug, projectId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, projectId]);
|
||||
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) return;
|
||||
@ -516,11 +573,12 @@ const useArchivedIssueActions = () => {
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
removeIssue,
|
||||
restoreIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, removeIssue, restoreIssue, updateFilters]
|
||||
[fetchIssues, fetchNextIssues, removeIssue, restoreIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
@ -530,26 +588,38 @@ const useGlobalIssueActions = () => {
|
||||
const {
|
||||
router: { workspaceSlug, globalViewId },
|
||||
} = useApplication();
|
||||
const fetchIssues = useCallback(
|
||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
return issues.fetchIssues(workspaceSlug.toString(), globalViewId.toString(), loadType, options);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug, globalViewId]
|
||||
);
|
||||
const fetchNextIssues = useCallback(async () => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
return issues.fetchNextIssues(workspaceSlug.toString(), globalViewId.toString());
|
||||
}, [issues.fetchIssues, workspaceSlug, globalViewId]);
|
||||
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, globalViewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
||||
},
|
||||
[issues.createIssue, globalViewId, workspaceSlug]
|
||||
[issues.createIssue, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, globalViewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, globalViewId, workspaceSlug]
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, globalViewId);
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, globalViewId, workspaceSlug]
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
@ -566,6 +636,8 @@ const useGlobalIssueActions = () => {
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
fetchNextIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { CycleDateCheckData, ICycle, TIssue } from "@plane/types";
|
||||
import type { CycleDateCheckData, ICycle, TIssue, TIssuesResponse } from "@plane/types";
|
||||
// helpers
|
||||
|
||||
export class CycleService extends APIService {
|
||||
@ -46,20 +46,12 @@ export class CycleService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getCycleIssues(workspaceSlug: string, projectId: string, cycleId: string): Promise<TIssue[]> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async getCycleIssuesWithParams(
|
||||
async getCycleIssues(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
cycleId: string,
|
||||
queries?: any
|
||||
): Promise<TIssue[]> {
|
||||
): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, {
|
||||
params: queries,
|
||||
})
|
||||
|
@ -2,7 +2,14 @@
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
import { APIService } from "services/api.service";
|
||||
// type
|
||||
import type { TIssue, IIssueDisplayProperties, TIssueLink, TIssueSubIssues, TIssueActivity } from "@plane/types";
|
||||
import type {
|
||||
TIssue,
|
||||
IIssueDisplayProperties,
|
||||
TIssueLink,
|
||||
TIssueSubIssues,
|
||||
TIssueActivity,
|
||||
TIssuesResponse,
|
||||
} from "@plane/types";
|
||||
// helper
|
||||
|
||||
export class IssueService extends APIService {
|
||||
@ -10,7 +17,7 @@ export class IssueService extends APIService {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async createIssue(workspaceSlug: string, projectId: string, data: any): Promise<any> {
|
||||
async createIssue(workspaceSlug: string, projectId: string, data: Partial<TIssue>): Promise<TIssue> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
@ -18,7 +25,7 @@ export class IssueService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<TIssue[]> {
|
||||
async getIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, {
|
||||
params: queries,
|
||||
})
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
import { APIService } from "services/api.service";
|
||||
// helpers
|
||||
import { TIssue } from "@plane/types";
|
||||
import { TIssue, TIssuesResponse } from "@plane/types";
|
||||
|
||||
export class IssueDraftService extends APIService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async getDraftIssues(workspaceSlug: string, projectId: string, query?: any): Promise<TIssue[]> {
|
||||
async getDraftIssues(workspaceSlug: string, projectId: string, query?: any): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, {
|
||||
params: { ...query },
|
||||
})
|
||||
@ -18,7 +18,7 @@ export class IssueDraftService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async createDraftIssue(workspaceSlug: string, projectId: string, data: any): Promise<any> {
|
||||
async createDraftIssue(workspaceSlug: string, projectId: string, data: any): Promise<TIssue> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
@ -26,7 +26,7 @@ export class IssueDraftService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async updateDraftIssue(workspaceSlug: string, projectId: string, issueId: string, data: any): Promise<any> {
|
||||
async updateDraftIssue(workspaceSlug: string, projectId: string, issueId: string, data: any): Promise<void> {
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
@ -34,7 +34,7 @@ export class IssueDraftService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async deleteDraftIssue(workspaceSlug: string, projectId: string, issueId: string): Promise<any> {
|
||||
async deleteDraftIssue(workspaceSlug: string, projectId: string, issueId: string): Promise<void> {
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
@ -42,7 +42,7 @@ export class IssueDraftService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getDraftIssueById(workspaceSlug: string, projectId: string, issueId: string, queries?: any): Promise<any> {
|
||||
async getDraftIssueById(workspaceSlug: string, projectId: string, issueId: string, queries?: any): Promise<TIssue> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`, {
|
||||
params: queries,
|
||||
})
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { IModule, TIssue, ILinkDetails, ModuleLink } from "@plane/types";
|
||||
import type { IModule, ILinkDetails, ModuleLink, TIssuesResponse } from "@plane/types";
|
||||
|
||||
export class ModuleService extends APIService {
|
||||
constructor() {
|
||||
@ -70,7 +70,12 @@ export class ModuleService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getModuleIssues(workspaceSlug: string, projectId: string, moduleId: string, queries?: any): Promise<TIssue[]> {
|
||||
async getModuleIssues(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
moduleId: string,
|
||||
queries?: any
|
||||
): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/issues/`, {
|
||||
params: queries,
|
||||
})
|
||||
@ -111,7 +116,7 @@ export class ModuleService extends APIService {
|
||||
projectId: string,
|
||||
moduleId: string,
|
||||
issueId: string
|
||||
): Promise<any> {
|
||||
): Promise<void> {
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/issues/${issueId}/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
@ -124,14 +129,14 @@ export class ModuleService extends APIService {
|
||||
projectId: string,
|
||||
moduleId: string,
|
||||
issueIds: string[]
|
||||
): Promise<any> {
|
||||
): Promise<void> {
|
||||
const promiseDataUrls: any = [];
|
||||
issueIds.forEach((issueId) => {
|
||||
promiseDataUrls.push(
|
||||
this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/issues/${issueId}/`)
|
||||
);
|
||||
});
|
||||
return await Promise.all(promiseDataUrls)
|
||||
await Promise.all(promiseDataUrls)
|
||||
.then((response) => response)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
@ -143,14 +148,14 @@ export class ModuleService extends APIService {
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleIds: string[]
|
||||
): Promise<any> {
|
||||
): Promise<void> {
|
||||
const promiseDataUrls: any = [];
|
||||
moduleIds.forEach((moduleId) => {
|
||||
promiseDataUrls.push(
|
||||
this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/issues/${issueId}/`)
|
||||
);
|
||||
});
|
||||
return await Promise.all(promiseDataUrls)
|
||||
await Promise.all(promiseDataUrls)
|
||||
.then((response) => response)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
|
@ -11,6 +11,7 @@ import type {
|
||||
IUserProfileProjectSegregation,
|
||||
IUserSettings,
|
||||
IUserEmailNotificationSettings,
|
||||
TIssuesResponse,
|
||||
} from "@plane/types";
|
||||
// helpers
|
||||
|
||||
@ -178,7 +179,7 @@ export class UserService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getUserProfileIssues(workspaceSlug: string, userId: string, params: any): Promise<TIssue[]> {
|
||||
async getUserProfileIssues(workspaceSlug: string, userId: string, params: any): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/user-issues/${userId}/`, {
|
||||
params,
|
||||
})
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
IWorkspaceBulkInviteFormData,
|
||||
IWorkspaceViewProps,
|
||||
IUserProjectsRole,
|
||||
TIssue,
|
||||
IWorkspaceView,
|
||||
TIssuesResponse,
|
||||
} from "@plane/types";
|
||||
|
||||
export class WorkspaceService extends APIService {
|
||||
@ -257,7 +257,7 @@ export class WorkspaceService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getViewIssues(workspaceSlug: string, params: any): Promise<TIssue[]> {
|
||||
async getViewIssues(workspaceSlug: string, params: any): Promise<TIssuesResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/issues/`, {
|
||||
params,
|
||||
})
|
||||
|
@ -14,20 +14,22 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface IArchivedIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines projectId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface IArchivedIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -92,6 +94,22 @@ export class ArchivedIssuesFilter extends IssueFilterHelperStore implements IArc
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
try {
|
||||
const _filters = this.handleIssuesLocalFilters.get(
|
||||
@ -150,7 +168,7 @@ export class ArchivedIssuesFilter extends IssueFilterHelperStore implements IArc
|
||||
});
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.archivedIssues.fetchIssues(
|
||||
this.rootIssueStore.archivedIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
@ -193,7 +211,7 @@ export class ArchivedIssuesFilter extends IssueFilterHelperStore implements IArc
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.archivedIssues.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.rootIssueStore.archivedIssues.fetchIssuesWithExistingPagination(workspaceSlug, projectId, "mutation");
|
||||
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.ARCHIVED, type, workspaceSlug, projectId, undefined, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,35 +1,36 @@
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
import { action, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
import { IssueArchiveService } from "services/issue";
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
// services
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { IArchivedIssuesFilter } from "./filter.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
|
||||
export interface IArchivedIssues {
|
||||
export interface IArchivedIssues extends IBaseIssuesStore {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [project_id: string]: string[] };
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
option: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
restoreIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
}
|
||||
|
||||
export class ArchivedIssues extends IssueHelperStore implements IArchivedIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [project_id: string]: string[] } = {};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// services
|
||||
archivedIssueService;
|
||||
export class ArchivedIssues extends BaseIssuesStore implements IArchivedIssues {
|
||||
// filter store
|
||||
issueFilterStore: IArchivedIssuesFilter;
|
||||
|
||||
//viewData
|
||||
viewFlags = {
|
||||
@ -38,99 +39,73 @@ export class ArchivedIssues extends IssueHelperStore implements IArchivedIssues
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IArchivedIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore, true);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
removeIssue: action,
|
||||
restoreIssue: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
// services
|
||||
this.archivedIssueService = new IssueArchiveService();
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const projectId = this.rootIssueStore.projectId;
|
||||
if (!projectId) return undefined;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.archivedIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const archivedIssueIds = this.issues[projectId];
|
||||
if (!archivedIssueIds) return undefined;
|
||||
|
||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(archivedIssueIds, "archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader") => {
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
options: IssuePaginationOptions
|
||||
) => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.archivedIssuesFilter?.appliedFilters;
|
||||
const response = await this.archivedIssueService.getArchivedIssues(workspaceSlug, projectId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[projectId],
|
||||
response.map((issue: TIssue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
this.loader = loadType;
|
||||
});
|
||||
this.clear();
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.issueArchiveService.getArchivedIssues(workspaceSlug, projectId, params);
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
this.loader = "pagination";
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[projectId], issueId);
|
||||
});
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
fetchIssuesWithExistingPagination = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "mutation"
|
||||
) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions);
|
||||
};
|
||||
|
||||
restoreIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
const response = await this.archivedIssueService.restoreIssue(workspaceSlug, projectId, issueId);
|
||||
const response = await this.issueArchiveService.restoreIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
this.rootStore.issues.updateIssue(issueId, {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, {
|
||||
archived_at: null,
|
||||
});
|
||||
pull(this.issues[projectId], issueId);
|
||||
this.issues && pull(this.issues, issueId);
|
||||
});
|
||||
|
||||
return response;
|
||||
@ -138,6 +113,4 @@ export class ArchivedIssues extends IssueHelperStore implements IArchivedIssues
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue: undefined;
|
||||
}
|
||||
|
@ -14,20 +14,22 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface ICycleIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines cycleId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface ICycleIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -95,6 +97,22 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string, cycleId: string) => {
|
||||
try {
|
||||
const _filters = await this.issueFilterService.fetchCycleIssueFilters(workspaceSlug, projectId, cycleId);
|
||||
@ -161,7 +179,7 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
|
||||
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.cycleIssues.fetchIssues(
|
||||
this.rootIssueStore.cycleIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation",
|
||||
@ -205,7 +223,12 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.cycleIssues.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
this.rootIssueStore.cycleIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
"mutation",
|
||||
cycleId
|
||||
);
|
||||
|
||||
await this.issueFilterService.patchCycleIssueFilters(workspaceSlug, projectId, cycleId, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,55 +1,43 @@
|
||||
import concat from "lodash/concat";
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
import { action, observable, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
// services
|
||||
import { CycleService } from "services/cycle.service";
|
||||
import { IssueService } from "services/issue";
|
||||
// types
|
||||
import { TIssue, TSubGroupedIssues, TGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TIssue, TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
import { ICycleIssuesFilter } from "./filter.store";
|
||||
|
||||
export const ACTIVE_CYCLE_ISSUES = "ACTIVE_CYCLE_ISSUES";
|
||||
|
||||
export interface ICycleIssues {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [cycle_id: string]: string[] };
|
||||
export interface ICycleIssues extends IBaseIssuesStore {
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
cycleId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
loadType: TLoader,
|
||||
cycleId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
cycleId?: string | undefined
|
||||
) => Promise<TIssue>;
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>, cycleId: string) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: TIssue) => Promise<TIssue | undefined>;
|
||||
removeBulkIssues: (workspaceSlug: string, projectId: string, issueIds: string[]) => Promise<void>;
|
||||
|
||||
addIssueToCycle: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -66,106 +54,63 @@ export interface ICycleIssues {
|
||||
new_cycle_id: string;
|
||||
}
|
||||
) => Promise<TIssue>;
|
||||
fetchActiveCycleIssues: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<TIssue[] | undefined>;
|
||||
fetchActiveCycleIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
cycleId: string
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
}
|
||||
|
||||
export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [cycle_id: string]: string[] } = {};
|
||||
export class CycleIssues extends BaseIssuesStore implements ICycleIssues {
|
||||
cycleId: string | undefined = undefined;
|
||||
viewFlags = {
|
||||
enableQuickAdd: true,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// service
|
||||
cycleService;
|
||||
issueService;
|
||||
// filter store
|
||||
issueFilterStore;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: ICycleIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
cycleId: observable.ref,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
quickAddIssue: action,
|
||||
|
||||
addIssueToCycle: action,
|
||||
removeIssueFromCycle: action,
|
||||
transferIssuesFromCycle: action,
|
||||
fetchActiveCycleIssues: action,
|
||||
});
|
||||
|
||||
this.rootIssueStore = _rootStore;
|
||||
this.issueService = new IssueService();
|
||||
// service
|
||||
this.cycleService = new CycleService();
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const cycleId = this.rootIssueStore?.cycleId;
|
||||
if (!cycleId) return undefined;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.cycleIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const cycleIssueIds = this.issues[cycleId];
|
||||
if (!cycleIssueIds) return;
|
||||
|
||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(cycleIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues = [];
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
} else if (layout === "calendar") issues = this.groupedIssues("target_date", "target_date", _issues, true);
|
||||
else if (layout === "spreadsheet") issues = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
else if (layout === "gantt_chart") issues = this.unGroupedIssues(orderBy ?? "sort_order", _issues);
|
||||
|
||||
return issues;
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
cycleId: string
|
||||
) => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.cycleIssuesFilter?.appliedFilters;
|
||||
const response = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, params);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[cycleId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
this.loader = loadType;
|
||||
});
|
||||
this.clear();
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
this.cycleId = cycleId;
|
||||
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.cycleService.getCycleIssues(workspaceSlug, projectId, cycleId, params);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
@ -173,7 +118,33 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, cycleId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string, cycleId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
this.loader = "pagination";
|
||||
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.cycleService.getCycleIssues(workspaceSlug, projectId, cycleId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
fetchIssuesWithExistingPagination = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
cycleId: string
|
||||
) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions, cycleId);
|
||||
};
|
||||
|
||||
override createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, cycleId: string) => {
|
||||
try {
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id], false);
|
||||
@ -185,81 +156,6 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId: string
|
||||
) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
const issueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === issueId);
|
||||
if (issueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[cycleId].splice(issueIndex, 1);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[cycleId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
cycleId: string | undefined = undefined
|
||||
) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[cycleId].push(data.id);
|
||||
this.rootIssueStore.issues.addIssue([data]);
|
||||
});
|
||||
|
||||
const response = await this.createIssue(workspaceSlug, projectId, data, cycleId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
const quickAddIssueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === data.id);
|
||||
if (quickAddIssueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[cycleId].splice(quickAddIssueIndex, 1);
|
||||
this.rootIssueStore.issues.removeIssue(data.id);
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (cycleId) this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
addIssueToCycle = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -275,17 +171,14 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds);
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, cycleId, (cycleIssueIds = []) => uniq(concat(cycleIssueIds, issueIds)));
|
||||
this.cycleId === cycleId &&
|
||||
update(this, "issues", (cycleIssueIds = []) => uniq(concat(cycleIssueIds, issueIds)));
|
||||
});
|
||||
|
||||
issueIds.forEach((issueId) => {
|
||||
const issueCycleId = this.rootIssueStore.issues.getIssueById(issueId)?.cycle_id;
|
||||
if (issueCycleId && issueCycleId !== cycleId) {
|
||||
runInAction(() => {
|
||||
pull(this.issues[issueCycleId], issueId);
|
||||
});
|
||||
}
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
||||
this.rootIssueStore.issues.updateIssue(issueId, { cycle_id: cycleId });
|
||||
});
|
||||
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -294,13 +187,12 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
|
||||
removeIssueFromCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => {
|
||||
try {
|
||||
await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
runInAction(() => {
|
||||
pull(this.issues[cycleId], issueId);
|
||||
this.issues && this.cycleId === cycleId && pull(this.issues, issueId);
|
||||
});
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: null });
|
||||
|
||||
await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
this.rootIssueStore.issues.updateIssue(issueId, { cycle_id: null });
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -322,7 +214,8 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
cycleId as string,
|
||||
payload
|
||||
);
|
||||
await this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
this.paginationOptions &&
|
||||
(await this.fetchIssues(workspaceSlug, projectId, "mutation", this.paginationOptions, cycleId));
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
@ -333,14 +226,14 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
fetchActiveCycleIssues = async (workspaceSlug: string, projectId: string, cycleId: string) => {
|
||||
try {
|
||||
const params = { priority: `urgent,high` };
|
||||
const response = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, params);
|
||||
const response = await this.cycleService.getCycleIssues(workspaceSlug, projectId, cycleId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(this.issues, [ACTIVE_CYCLE_ISSUES], Object.keys(response));
|
||||
this.loader = undefined;
|
||||
});
|
||||
// runInAction(() => {
|
||||
// set(this.issues, , Object.keys(response));
|
||||
// this.loader = undefined;
|
||||
// });
|
||||
|
||||
this.rootIssueStore.issues.addIssue(Object.values(response));
|
||||
// this.rootIssueStore.issues.addIssue(Object.values(response));
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
|
@ -14,20 +14,22 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface IDraftIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines projectId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface IDraftIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -92,6 +94,22 @@ export class DraftIssuesFilter extends IssueFilterHelperStore implements IDraftI
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
try {
|
||||
const _filters = this.handleIssuesLocalFilters.get(EIssuesStoreType.DRAFT, workspaceSlug, projectId, undefined);
|
||||
@ -145,7 +163,7 @@ export class DraftIssuesFilter extends IssueFilterHelperStore implements IDraftI
|
||||
});
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.draftIssues.fetchIssues(
|
||||
this.rootIssueStore.draftIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
@ -188,7 +206,7 @@ export class DraftIssuesFilter extends IssueFilterHelperStore implements IDraftI
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.draftIssues.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.rootIssueStore.draftIssues.fetchIssuesWithExistingPagination(workspaceSlug, projectId, "mutation");
|
||||
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,178 +1,103 @@
|
||||
import concat from "lodash/concat";
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
import { action, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
// services
|
||||
import { IssueDraftService } from "services/issue/issue_draft.service";
|
||||
// types
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TIssue, TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { IDraftIssuesFilter } from "./filter.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
|
||||
export interface IDraftIssues {
|
||||
export interface IDraftIssues extends IBaseIssuesStore {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [project_id: string]: string[] };
|
||||
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
option: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string) => Promise<TIssuesResponse | undefined>;
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
}
|
||||
|
||||
export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [project_id: string]: string[] } = {};
|
||||
export class DraftIssues extends BaseIssuesStore implements IDraftIssues {
|
||||
viewFlags = {
|
||||
enableQuickAdd: false,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// service
|
||||
issueDraftService;
|
||||
// filter store
|
||||
issueFilterStore: IDraftIssuesFilter;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IDraftIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
this.issueDraftService = new IssueDraftService();
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
get getIssues() {
|
||||
const projectId = this.rootIssueStore.projectId;
|
||||
if (!projectId || !this.issues || !this.issues[projectId]) return undefined;
|
||||
|
||||
return this.issues[projectId];
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const projectId = this.rootIssueStore.projectId;
|
||||
if (!projectId) return undefined;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.draftIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const draftIssueIds = this.issues[projectId];
|
||||
if (!draftIssueIds) return undefined;
|
||||
|
||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(draftIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader") => {
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
options: IssuePaginationOptions
|
||||
) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.draftIssuesFilter?.appliedFilters;
|
||||
});
|
||||
this.clear();
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.issueDraftService.getDraftIssues(workspaceSlug, projectId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[projectId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data);
|
||||
this.loader = "pagination";
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, [projectId], (issueIds = []) => uniq(concat(issueIds, response.id)));
|
||||
});
|
||||
|
||||
this.rootStore.issues.addIssue([response]);
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
try {
|
||||
await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
|
||||
if (data.hasOwnProperty("is_draft") && data?.is_draft === false) {
|
||||
runInAction(() => {
|
||||
update(this.issues, [projectId], (issueIds = []) => {
|
||||
if (issueIds.includes(issueId)) pull(issueIds, issueId);
|
||||
return issueIds;
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
throw error;
|
||||
}
|
||||
fetchIssuesWithExistingPagination = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "mutation"
|
||||
) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions);
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, [projectId], (issueIds = []) => {
|
||||
if (issueIds.includes(issueId)) pull(issueIds, issueId);
|
||||
return issueIds;
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue: undefined;
|
||||
createIssue = this.createDraftIssue;
|
||||
updateIssue = this.updateDraftIssue;
|
||||
}
|
||||
|
@ -1,72 +1,387 @@
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import update from "lodash/update";
|
||||
import uniq from "lodash/uniq";
|
||||
import concat from "lodash/concat";
|
||||
import pull from "lodash/pull";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import get from "lodash/get";
|
||||
import indexOf from "lodash/indexOf";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import values from "lodash/values";
|
||||
// types
|
||||
import {
|
||||
TIssue,
|
||||
TIssueMap,
|
||||
TIssueGroupByOptions,
|
||||
TIssueOrderByOptions,
|
||||
TGroupedIssues,
|
||||
TSubGroupedIssues,
|
||||
TUnGroupedIssues,
|
||||
TLoader,
|
||||
IssuePaginationOptions,
|
||||
TIssuesResponse,
|
||||
} from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { IBaseIssueFilterStore } from "./issue-filter-helper.store";
|
||||
// constants
|
||||
import { ISSUE_PRIORITIES } from "constants/issue";
|
||||
import { STATE_GROUPS } from "constants/state";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { TIssue, TIssueMap, TIssueGroupByOptions, TIssueOrderByOptions } from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
// services
|
||||
import { IssueArchiveService, IssueDraftService, IssueService } from "services/issue";
|
||||
|
||||
export type TIssueDisplayFilterOptions = Exclude<TIssueGroupByOptions, null> | "target_date";
|
||||
|
||||
export type TIssueHelperStore = {
|
||||
export interface IBaseIssuesStore {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
|
||||
issues: string[] | undefined;
|
||||
|
||||
nextCursor: string | undefined;
|
||||
prevCursor: string | undefined;
|
||||
issueCount: number | undefined;
|
||||
pageCount: number | undefined;
|
||||
|
||||
groupedIssueCount: Record<string, number> | undefined;
|
||||
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
|
||||
//actions
|
||||
removeIssue(workspaceSlug: string, projectId: string, issueId: string): Promise<void>;
|
||||
// helper methods
|
||||
groupedIssues(
|
||||
groupBy: TIssueDisplayFilterOptions,
|
||||
orderBy: TIssueOrderByOptions,
|
||||
issues: TIssueMap,
|
||||
groupedIssueCount: Record<string, number>,
|
||||
isCalendarIssues?: boolean
|
||||
): { [group_id: string]: string[] };
|
||||
): TGroupedIssues;
|
||||
subGroupedIssues(
|
||||
subGroupBy: TIssueDisplayFilterOptions,
|
||||
groupBy: TIssueDisplayFilterOptions,
|
||||
orderBy: TIssueOrderByOptions,
|
||||
issues: TIssueMap
|
||||
): { [sub_group_id: string]: { [group_id: string]: string[] } };
|
||||
unGroupedIssues(orderBy: TIssueOrderByOptions, issues: TIssueMap): string[];
|
||||
issues: TIssueMap,
|
||||
groupedIssueCount: Record<string, number>
|
||||
): TSubGroupedIssues;
|
||||
unGroupedIssues(orderBy: TIssueOrderByOptions, issues: TIssueMap, count: number): TUnGroupedIssues;
|
||||
issueDisplayFiltersDefaultData(groupBy: string | null): string[];
|
||||
issuesSortWithOrderBy(issueObject: TIssueMap, key: Partial<TIssueOrderByOptions>): TIssue[];
|
||||
getGroupArray(value: boolean | number | string | string[] | null, isDate?: boolean): string[];
|
||||
};
|
||||
}
|
||||
|
||||
const ISSUE_FILTER_DEFAULT_DATA: Record<TIssueDisplayFilterOptions, keyof TIssue> = {
|
||||
project: "project_id",
|
||||
cycle: "cycle_id",
|
||||
module: "module_ids",
|
||||
state: "state_id",
|
||||
"state_detail.group": "state_group" as keyof TIssue, // state_detail.group is only being used for state_group display,
|
||||
priority: "priority",
|
||||
labels: "label_ids",
|
||||
created_by: "created_by",
|
||||
assignees: "assignee_ids",
|
||||
mentions: "assignee_ids",
|
||||
target_date: "target_date",
|
||||
cycle: "cycle_id",
|
||||
module: "module_ids",
|
||||
};
|
||||
|
||||
export class IssueHelperStore implements TIssueHelperStore {
|
||||
// root store
|
||||
rootStore;
|
||||
export class BaseIssuesStore implements IBaseIssuesStore {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: string[] | undefined = undefined;
|
||||
groupedIssueCount: Record<string, number> | undefined = undefined;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
this.rootStore = _rootStore;
|
||||
nextCursor: string | undefined = undefined;
|
||||
prevCursor: string | undefined = undefined;
|
||||
|
||||
issueCount: number | undefined = undefined;
|
||||
pageCount: number | undefined = undefined;
|
||||
|
||||
paginationOptions: IssuePaginationOptions | undefined = undefined;
|
||||
|
||||
isArchived: boolean;
|
||||
|
||||
// services
|
||||
issueService;
|
||||
issueArchiveService;
|
||||
issueDraftService;
|
||||
// root store
|
||||
rootIssueStore;
|
||||
issueFilterStore;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IBaseIssueFilterStore, isArchived = false) {
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
groupedIssueCount: observable,
|
||||
issues: observable,
|
||||
|
||||
nextCursor: observable.ref,
|
||||
prevCursor: observable.ref,
|
||||
issueCount: observable.ref,
|
||||
pageCount: observable.ref,
|
||||
|
||||
paginationOptions: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
storePreviousPaginationValues: action.bound,
|
||||
|
||||
onfetchIssues: action.bound,
|
||||
onfetchNexIssues: action.bound,
|
||||
clear: action.bound,
|
||||
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
quickAddIssue: action,
|
||||
removeBulkIssues: action,
|
||||
});
|
||||
this.rootIssueStore = _rootStore;
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
|
||||
this.isArchived = isArchived;
|
||||
|
||||
this.issueService = new IssueService();
|
||||
this.issueArchiveService = new IssueArchiveService();
|
||||
this.issueDraftService = new IssueDraftService();
|
||||
}
|
||||
|
||||
storePreviousPaginationValues = (issuesResponse: TIssuesResponse, options?: IssuePaginationOptions) => {
|
||||
if (options) this.paginationOptions = options;
|
||||
|
||||
this.nextCursor = issuesResponse.next_cursor;
|
||||
this.prevCursor = issuesResponse.prev_cursor;
|
||||
|
||||
this.issueCount = issuesResponse.count;
|
||||
this.pageCount = issuesResponse.total_pages;
|
||||
};
|
||||
|
||||
get groupedIssueIds() {
|
||||
const displayFilters = this.issueFilterStore?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
if (!this.issues) return;
|
||||
|
||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(
|
||||
this.issues,
|
||||
this.isArchived ? "archived" : "un-archived"
|
||||
);
|
||||
if (!_issues) return {};
|
||||
|
||||
let groupedIssues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues = {};
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) groupedIssues = this.groupedIssues(groupBy, orderBy, _issues, this.groupedIssueCount);
|
||||
else groupedIssues = this.unGroupedIssues(orderBy, _issues, this.issueCount);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy)
|
||||
groupedIssues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues, this.groupedIssueCount);
|
||||
else groupedIssues = this.groupedIssues(groupBy, orderBy, _issues, this.groupedIssueCount);
|
||||
} else if (layout === "calendar")
|
||||
groupedIssues = this.groupedIssues("target_date", "target_date", _issues, this.groupedIssueCount, true);
|
||||
else if (layout === "spreadsheet")
|
||||
groupedIssues = this.unGroupedIssues(orderBy ?? "-created_at", _issues, this.issueCount);
|
||||
else if (layout === "gantt_chart")
|
||||
groupedIssues = this.unGroupedIssues(orderBy ?? "sort_order", _issues, this.issueCount);
|
||||
|
||||
return groupedIssues;
|
||||
}
|
||||
|
||||
onfetchIssues(issuesResponse: TIssuesResponse, options: IssuePaginationOptions) {
|
||||
const { issueList, groupedIssueCount } = this.processIssueResponse(issuesResponse);
|
||||
|
||||
runInAction(() => {
|
||||
this.issues = issueList.map((issue) => issue.id);
|
||||
this.groupedIssueCount = groupedIssueCount;
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(issueList);
|
||||
|
||||
this.storePreviousPaginationValues(issuesResponse, options);
|
||||
}
|
||||
|
||||
onfetchNexIssues(issuesResponse: TIssuesResponse) {
|
||||
const { issueList, groupedIssueCount } = this.processIssueResponse(issuesResponse);
|
||||
const newIssueIds = issueList.map((issue) => issue.id);
|
||||
|
||||
runInAction(() => {
|
||||
update(this, "issues", (issueIds: string[] = []) => {
|
||||
return uniq(concat(issueIds, newIssueIds));
|
||||
});
|
||||
|
||||
this.groupedIssueCount = groupedIssueCount;
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(issueList);
|
||||
|
||||
this.storePreviousPaginationValues(issuesResponse);
|
||||
}
|
||||
|
||||
async createIssue(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
id?: string,
|
||||
shouldAddStore = true
|
||||
) {
|
||||
try {
|
||||
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
||||
|
||||
if (shouldAddStore) this.addIssue(response);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateIssue(workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) {
|
||||
const issueBeforeUpdate = { ...this.rootIssueStore.issues.getIssueById(issueId) };
|
||||
try {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, data);
|
||||
|
||||
await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, issueBeforeUpdate);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async createDraftIssue(workspaceSlug: string, projectId: string, data: Partial<TIssue>) {
|
||||
try {
|
||||
const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data);
|
||||
|
||||
this.addIssue(response);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateDraftIssue(workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) {
|
||||
const issueBeforeUpdate = { ...this.rootIssueStore.issues.getIssueById(issueId) };
|
||||
try {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, data);
|
||||
|
||||
await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, issueBeforeUpdate);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async removeIssue(workspaceSlug: string, projectId: string, issueId: string) {
|
||||
try {
|
||||
await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
if (this.issues) pull(this.issues, issueId);
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.removeIssue(issueId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async archiveIssue(workspaceSlug: string, projectId: string, issueId: string) {
|
||||
try {
|
||||
const response = await this.issueArchiveService.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
this.rootIssueStore.issues.updateIssue(issueId, {
|
||||
archived_at: response.archived_at,
|
||||
});
|
||||
if (this.issues) pull(this.issues, issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async quickAddIssue(workspaceSlug: string, projectId: string, data: TIssue) {
|
||||
if (!this.issues) this.issues = [];
|
||||
try {
|
||||
this.addIssue(data);
|
||||
|
||||
const response = await this.createIssue(workspaceSlug, projectId, data);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
if (!this.issues) return;
|
||||
const quickAddIssueIndex = this.issues.findIndex((_issueId) => _issueId === data.id);
|
||||
if (quickAddIssueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues!.splice(quickAddIssueIndex, 1);
|
||||
this.rootIssueStore.issues.removeIssue(data.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async removeBulkIssues(workspaceSlug: string, projectId: string, issueIds: string[]) {
|
||||
try {
|
||||
if (!this.issues) return;
|
||||
|
||||
const response = await this.issueService.bulkDeleteIssues(workspaceSlug, projectId, { issue_ids: issueIds });
|
||||
|
||||
runInAction(() => {
|
||||
issueIds.forEach((issueId) => {
|
||||
pull(this.issues!, issueId);
|
||||
this.rootIssueStore.issues.removeIssue(issueId);
|
||||
});
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
addIssue(issue: TIssue) {
|
||||
runInAction(() => {
|
||||
if (!this.issues) this.issues = [];
|
||||
this.issues.push(issue.id);
|
||||
this.rootIssueStore.issues.addIssue([issue]);
|
||||
});
|
||||
}
|
||||
|
||||
clear() {
|
||||
runInAction(() => {
|
||||
this.issues = undefined;
|
||||
this.groupedIssueCount = undefined;
|
||||
this.groupedIssueCount = undefined;
|
||||
|
||||
this.nextCursor = undefined;
|
||||
this.prevCursor = undefined;
|
||||
|
||||
this.issueCount = undefined;
|
||||
this.pageCount = undefined;
|
||||
|
||||
this.paginationOptions = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
groupedIssues = (
|
||||
groupBy: TIssueDisplayFilterOptions,
|
||||
orderBy: TIssueOrderByOptions,
|
||||
issues: TIssueMap,
|
||||
groupedIssueCount: Record<string, number> | undefined,
|
||||
isCalendarIssues: boolean = false
|
||||
) => {
|
||||
const _issues: { [group_id: string]: string[] } = {};
|
||||
if (!groupBy) return _issues;
|
||||
const _issues: TGroupedIssues = {};
|
||||
if (!groupBy || !groupedIssueCount) return _issues;
|
||||
|
||||
this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => {
|
||||
_issues[group] = [];
|
||||
_issues[group] = { issueIds: [], issueCount: groupedIssueCount[group] };
|
||||
});
|
||||
|
||||
const projectIssues = this.issuesSortWithOrderBy(issues, orderBy);
|
||||
@ -76,8 +391,8 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
let groupArray = [];
|
||||
|
||||
if (groupBy === "state_detail.group") {
|
||||
// if groupBy state_detail.group is coming from the project level the we are using stateDetails from root store else we are looping through the stateMap
|
||||
const state_group = (this.rootStore?.stateMap || {})?.[_issue?.state_id]?.group || "None";
|
||||
const state_group =
|
||||
this.rootIssueStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None";
|
||||
groupArray = [state_group];
|
||||
} else {
|
||||
const groupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[groupBy]);
|
||||
@ -85,8 +400,8 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
|
||||
for (const group of groupArray) {
|
||||
if (group && _issues[group]) _issues[group].push(_issue.id);
|
||||
else if (group) _issues[group] = [_issue.id];
|
||||
if (group && _issues[group]) _issues[group].issueIds.push(_issue.id);
|
||||
else if (group) _issues[group].issueIds = [_issue.id];
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,15 +412,16 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
subGroupBy: TIssueDisplayFilterOptions,
|
||||
groupBy: TIssueDisplayFilterOptions,
|
||||
orderBy: TIssueOrderByOptions,
|
||||
issues: TIssueMap
|
||||
issues: TIssueMap,
|
||||
groupedIssueCount: Record<string, number> | undefined
|
||||
) => {
|
||||
const _issues: { [sub_group_id: string]: { [group_id: string]: string[] } } = {};
|
||||
if (!subGroupBy || !groupBy) return _issues;
|
||||
const _issues: TSubGroupedIssues = {};
|
||||
if (!subGroupBy || !groupBy || !groupedIssueCount) return _issues;
|
||||
|
||||
this.issueDisplayFiltersDefaultData(subGroupBy).forEach((sub_group: any) => {
|
||||
const groupByIssues: { [group_id: string]: string[] } = {};
|
||||
const groupByIssues: TGroupedIssues = {};
|
||||
this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => {
|
||||
groupByIssues[group] = [];
|
||||
groupByIssues[group] = { issueIds: [], issueCount: groupedIssueCount[group] };
|
||||
});
|
||||
_issues[sub_group] = groupByIssues;
|
||||
});
|
||||
@ -117,8 +433,8 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
let subGroupArray = [];
|
||||
let groupArray = [];
|
||||
if (subGroupBy === "state_detail.group" || groupBy === "state_detail.group") {
|
||||
const state_group = (this.rootStore?.stateMap || {})?.[_issue?.state_id]?.group || "None";
|
||||
|
||||
const state_group =
|
||||
this.rootIssueStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None";
|
||||
subGroupArray = [state_group];
|
||||
groupArray = [state_group];
|
||||
} else {
|
||||
@ -130,9 +446,10 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
|
||||
for (const subGroup of subGroupArray) {
|
||||
for (const group of groupArray) {
|
||||
if (subGroup && group && _issues?.[subGroup]?.[group]) _issues[subGroup][group].push(_issue.id);
|
||||
else if (subGroup && group && _issues[subGroup]) _issues[subGroup][group] = [_issue.id];
|
||||
else if (subGroup && group) _issues[subGroup] = { [group]: [_issue.id] };
|
||||
if (subGroup && group && _issues?.[subGroup]?.[group]) _issues[subGroup][group].issueIds.push(_issue.id);
|
||||
else if (subGroup && group && _issues[subGroup]) _issues[subGroup][group].issueIds = [_issue.id];
|
||||
else if (subGroup && group)
|
||||
_issues[subGroup] = { [group]: { issueIds: [_issue.id], issueCount: groupedIssueCount[group] } };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,29 +457,28 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
return _issues;
|
||||
};
|
||||
|
||||
unGroupedIssues = (orderBy: TIssueOrderByOptions, issues: TIssueMap) =>
|
||||
this.issuesSortWithOrderBy(issues, orderBy).map((issue) => issue.id);
|
||||
unGroupedIssues = (orderBy: TIssueOrderByOptions, issues: TIssueMap, count: number | undefined) => {
|
||||
const issueIds = this.issuesSortWithOrderBy(issues, orderBy).map((issue) => issue.id);
|
||||
|
||||
return { "All Issues": { issueIds, issueCount: count || issueIds.length } };
|
||||
};
|
||||
|
||||
issueDisplayFiltersDefaultData = (groupBy: string | null): string[] => {
|
||||
switch (groupBy) {
|
||||
case "state":
|
||||
return Object.keys(this.rootStore?.stateMap || {});
|
||||
return Object.keys(this.rootIssueStore?.stateMap || {});
|
||||
case "state_detail.group":
|
||||
return Object.keys(STATE_GROUPS);
|
||||
case "priority":
|
||||
return ISSUE_PRIORITIES.map((i) => i.key);
|
||||
case "labels":
|
||||
return Object.keys(this.rootStore?.labelMap || {});
|
||||
return Object.keys(this.rootIssueStore?.labelMap || {});
|
||||
case "created_by":
|
||||
return Object.keys(this.rootStore?.workSpaceMemberRolesMap || {});
|
||||
return Object.keys(this.rootIssueStore?.workSpaceMemberRolesMap || {});
|
||||
case "assignees":
|
||||
return Object.keys(this.rootStore?.workSpaceMemberRolesMap || {});
|
||||
return Object.keys(this.rootIssueStore?.workSpaceMemberRolesMap || {});
|
||||
case "project":
|
||||
return Object.keys(this.rootStore?.projectMap || {});
|
||||
case "cycle":
|
||||
return Object.keys(this.rootStore?.cycleMap || {});
|
||||
case "module":
|
||||
return Object.keys(this.rootStore?.moduleMap || {});
|
||||
return Object.keys(this.rootIssueStore?.projectMap || {});
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@ -188,7 +504,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
|
||||
switch (dataType) {
|
||||
case "state_id":
|
||||
const stateMap = this.rootStore?.stateMap;
|
||||
const stateMap = this.rootIssueStore?.stateMap;
|
||||
if (!stateMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const state = stateMap[dataId];
|
||||
@ -196,7 +512,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
break;
|
||||
case "label_ids":
|
||||
const labelMap = this.rootStore?.labelMap;
|
||||
const labelMap = this.rootIssueStore?.labelMap;
|
||||
if (!labelMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const label = labelMap[dataId];
|
||||
@ -204,7 +520,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
break;
|
||||
case "assignee_ids":
|
||||
const memberMap = this.rootStore?.memberMap;
|
||||
const memberMap = this.rootIssueStore?.memberMap;
|
||||
if (!memberMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const member = memberMap[dataId];
|
||||
@ -212,7 +528,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
break;
|
||||
case "module_ids":
|
||||
const moduleMap = this.rootStore?.moduleMap;
|
||||
const moduleMap = this.rootIssueStore?.moduleMap;
|
||||
if (!moduleMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const _module = moduleMap[dataId];
|
||||
@ -220,7 +536,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
break;
|
||||
case "cycle_id":
|
||||
const cycleMap = this.rootStore?.cycleMap;
|
||||
const cycleMap = this.rootIssueStore?.cycleMap;
|
||||
if (!cycleMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const cycle = cycleMap[dataId];
|
||||
@ -233,10 +549,10 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* This Method is mainly used to filter out empty values in the beginning
|
||||
* This Method is mainly used to filter out empty values in the begining
|
||||
* @param key key of the value that is to be checked if empty
|
||||
* @param object any object in which the key's value is to be checked
|
||||
* @returns 1 if empty, 0 if not empty
|
||||
* @returns 1 if emoty, 0 if not empty
|
||||
*/
|
||||
getSortOrderToFilterEmptyValues(key: string, object: any) {
|
||||
const value = object?.[key];
|
||||
@ -248,7 +564,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
|
||||
issuesSortWithOrderBy = (issueObject: TIssueMap, key: Partial<TIssueOrderByOptions>): TIssue[] => {
|
||||
let array = values(issueObject);
|
||||
array = orderBy(array, "created_at");
|
||||
array = orderBy(array, "created_at", ["asc"]);
|
||||
|
||||
switch (key) {
|
||||
case "sort_order":
|
||||
@ -395,4 +711,38 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
else if (isDate) return [renderFormattedPayloadDate(value) || "None"];
|
||||
else return [value || "None"];
|
||||
}
|
||||
|
||||
processIssueResponse(issueResponse: TIssuesResponse): {
|
||||
issueList: TIssue[];
|
||||
groupedIssueCount: Record<string, number>;
|
||||
} {
|
||||
const issueResult = issueResponse?.results;
|
||||
|
||||
if (!issueResult)
|
||||
return {
|
||||
issueList: [],
|
||||
groupedIssueCount: {},
|
||||
};
|
||||
|
||||
if (Array.isArray(issueResult)) {
|
||||
return {
|
||||
issueList: issueResult,
|
||||
groupedIssueCount: { "All Issues": issueResponse.count },
|
||||
};
|
||||
}
|
||||
|
||||
const issueList: TIssue[] = [];
|
||||
const groupedIssueCount: Record<string, number> = {};
|
||||
|
||||
for (const groupId in issueResult) {
|
||||
const groupIssueResult = issueResult[groupId];
|
||||
|
||||
if (!groupIssueResult) continue;
|
||||
|
||||
issueList.push(...groupIssueResult.results);
|
||||
groupedIssueCount[groupId] = groupIssueResult.total_results;
|
||||
}
|
||||
|
||||
return { issueList, groupedIssueCount };
|
||||
}
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
// types
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
// lib
|
||||
import { storage } from "lib/local-storage";
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
@ -14,6 +10,10 @@ import {
|
||||
TIssueParams,
|
||||
TStaticViewTypes,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType, IssueGroupByOptions } from "constants/issue";
|
||||
// lib
|
||||
import { storage } from "lib/local-storage";
|
||||
|
||||
interface ILocalStoreIssueFilters {
|
||||
key: EIssuesStoreType;
|
||||
@ -23,6 +23,14 @@ interface ILocalStoreIssueFilters {
|
||||
filters: IIssueFilters;
|
||||
}
|
||||
|
||||
export interface IBaseIssueFilterStore {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>;
|
||||
//computed
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
}
|
||||
|
||||
export interface IIssueFilterHelperStore {
|
||||
computedIssueFilters(filters: IIssueFilters): IIssueFilters;
|
||||
computedFilteredParams(
|
||||
@ -78,9 +86,11 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
|
||||
module: filters?.module || undefined,
|
||||
start_date: filters?.start_date || undefined,
|
||||
target_date: filters?.target_date || undefined,
|
||||
project: filters.project || undefined,
|
||||
subscriber: filters.subscriber || undefined,
|
||||
project: filters?.project || undefined,
|
||||
subscriber: filters?.subscriber || undefined,
|
||||
// display filters
|
||||
group_by: displayFilters?.group_by ? IssueGroupByOptions[displayFilters.group_by] : undefined,
|
||||
order_by: displayFilters?.order_by || undefined,
|
||||
type: displayFilters?.type || undefined,
|
||||
sub_issue: displayFilters?.sub_issue ?? true,
|
||||
};
|
||||
|
@ -14,20 +14,22 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface IModuleIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines moduleId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface IModuleIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string, moduleId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -95,6 +97,22 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string, moduleId: string) => {
|
||||
try {
|
||||
const _filters = await this.issueFilterService.fetchModuleIssueFilters(workspaceSlug, projectId, moduleId);
|
||||
@ -160,7 +178,7 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
|
||||
});
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.moduleIssues.fetchIssues(
|
||||
this.rootIssueStore.moduleIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation",
|
||||
@ -204,7 +222,12 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.moduleIssues.fetchIssues(workspaceSlug, projectId, "mutation", moduleId);
|
||||
this.rootIssueStore.moduleIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
"mutation",
|
||||
moduleId
|
||||
);
|
||||
|
||||
await this.issueFilterService.patchModuleIssueFilters(workspaceSlug, projectId, moduleId, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,53 +1,41 @@
|
||||
import concat from "lodash/concat";
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
// base class
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
import { ModuleService } from "services/module.service";
|
||||
// types
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TIssue, TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { IModuleIssuesFilter } from "./filter.store";
|
||||
|
||||
export interface IModuleIssues {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [module_id: string]: string[] };
|
||||
export interface IModuleIssues extends IBaseIssuesStore {
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
moduleId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
loadType: TLoader,
|
||||
moduleId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
moduleId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
moduleId?: string | undefined
|
||||
) => Promise<TIssue | undefined>;
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string, moduleId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>, moduleId: string) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: TIssue) => Promise<TIssue | undefined>;
|
||||
removeBulkIssues: (workspaceSlug: string, projectId: string, issueIds: string[]) => Promise<void>;
|
||||
|
||||
addIssuesToModule: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -71,193 +59,99 @@ export interface IModuleIssues {
|
||||
removeIssueFromModule: (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [module_id: string]: string[] } = {};
|
||||
export class ModuleIssues extends BaseIssuesStore implements IModuleIssues {
|
||||
moduleId: string | undefined = undefined;
|
||||
viewFlags = {
|
||||
enableQuickAdd: true,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// service
|
||||
moduleService;
|
||||
issueService;
|
||||
// filter store
|
||||
issueFilterStore: IModuleIssuesFilter;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IModuleIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
moduleId: observable.ref,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
quickAddIssue: action,
|
||||
|
||||
addIssuesToModule: action,
|
||||
removeIssuesFromModule: action,
|
||||
addModulesToIssue: action,
|
||||
removeModulesFromIssue: action,
|
||||
removeIssueFromModule: action,
|
||||
});
|
||||
|
||||
this.rootIssueStore = _rootStore;
|
||||
this.issueService = new IssueService();
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
// service
|
||||
this.moduleService = new ModuleService();
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const moduleId = this.rootIssueStore?.moduleId;
|
||||
if (!moduleId) return undefined;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.moduleIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const moduleIssueIds = this.issues[moduleId];
|
||||
if (!moduleIssueIds) return;
|
||||
|
||||
const _issues = this.rootIssueStore.issues.getIssuesByIds(moduleIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues = [];
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
} else if (layout === "calendar") issues = this.groupedIssues("target_date", "target_date", _issues, true);
|
||||
else if (layout === "spreadsheet") issues = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
else if (layout === "gantt_chart") issues = this.unGroupedIssues(orderBy ?? "sort_order", _issues);
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
moduleId: string
|
||||
) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.moduleIssuesFilter?.appliedFilters;
|
||||
const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[moduleId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
this.clear();
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
this.moduleId = moduleId;
|
||||
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, moduleId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string, moduleId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id], false);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
this.loader = "pagination";
|
||||
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (
|
||||
fetchIssuesWithExistingPagination = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
loadType: TLoader,
|
||||
moduleId: string
|
||||
) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", moduleId);
|
||||
throw error;
|
||||
}
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions, moduleId);
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => {
|
||||
override createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, moduleId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
const response = await super.createIssue(workspaceSlug, projectId, data, moduleId, false);
|
||||
await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id], false);
|
||||
this.moduleId === moduleId && this.addIssue(response);
|
||||
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
const issueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === issueId);
|
||||
if (issueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[moduleId].splice(issueIndex, 1);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[moduleId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
moduleId: string | undefined = undefined
|
||||
) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[moduleId].push(data.id);
|
||||
this.rootIssueStore.issues.addIssue([data]);
|
||||
});
|
||||
|
||||
const response = await this.createIssue(workspaceSlug, projectId, data, moduleId);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
const quickAddIssueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === data.id);
|
||||
if (quickAddIssueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[moduleId].splice(quickAddIssueIndex, 1);
|
||||
this.rootIssueStore.issues.removeIssue(data.id);
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -279,18 +173,20 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
if (fetchAddedIssues) await this.rootIssueStore.issues.getIssues(workspaceSlug, projectId, issueIds);
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
this.moduleId === moduleId &&
|
||||
update(this, "issues", (moduleIssueIds = []) => {
|
||||
if (!moduleIssueIds) return [...issueIds];
|
||||
else return uniq(concat(moduleIssueIds, issueIds));
|
||||
});
|
||||
});
|
||||
|
||||
issueIds.forEach((issueId) => {
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) => {
|
||||
update(this.rootIssueStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) => {
|
||||
if (issueModuleIds.includes(moduleId)) return issueModuleIds;
|
||||
else return uniq(concat(issueModuleIds, [moduleId]));
|
||||
});
|
||||
});
|
||||
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -299,27 +195,29 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
|
||||
removeIssuesFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
issueIds.forEach((issueId) => {
|
||||
pull(this.issues[moduleId], issueId);
|
||||
});
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
issueIds.forEach((issueId) => {
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) => {
|
||||
if (issueModuleIds.includes(moduleId)) return pull(issueModuleIds, moduleId);
|
||||
else return uniq(concat(issueModuleIds, [moduleId]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const response = await this.moduleService.removeIssuesFromModuleBulk(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
moduleId,
|
||||
issueIds
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
this.moduleId === moduleId &&
|
||||
issueIds.forEach((issueId) => {
|
||||
this.issues && pull(this.issues, issueId);
|
||||
});
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
issueIds.forEach((issueId) => {
|
||||
update(this.rootIssueStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) => {
|
||||
if (issueModuleIds.includes(moduleId)) return pull(issueModuleIds, moduleId);
|
||||
else return uniq(concat(issueModuleIds, [moduleId]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
return response;
|
||||
@ -336,12 +234,13 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
|
||||
runInAction(() => {
|
||||
moduleIds.forEach((moduleId) => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
this.moduleId === moduleId &&
|
||||
update(this, "issues", (moduleIssueIds = []) => {
|
||||
if (moduleIssueIds.includes(issueId)) return moduleIssueIds;
|
||||
else return uniq(concat(moduleIssueIds, [issueId]));
|
||||
});
|
||||
});
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
update(this.rootIssueStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
uniq(concat(issueModuleIds, moduleIds))
|
||||
);
|
||||
});
|
||||
@ -356,11 +255,12 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
try {
|
||||
runInAction(() => {
|
||||
moduleIds.forEach((moduleId) => {
|
||||
update(this.issues, moduleId, (moduleIssueIds = []) => {
|
||||
this.moduleId === moduleId &&
|
||||
update(this, "issues", (moduleIssueIds = []) => {
|
||||
if (moduleIssueIds.includes(issueId)) return pull(moduleIssueIds, issueId);
|
||||
else return uniq(concat(moduleIssueIds, [issueId]));
|
||||
});
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
update(this.rootIssueStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
pull(issueModuleIds, moduleId)
|
||||
);
|
||||
});
|
||||
@ -382,8 +282,8 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
removeIssueFromModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
pull(this.issues[moduleId], issueId);
|
||||
update(this.rootStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
this.issues && this.moduleId === this.moduleId && pull(this.issues, issueId);
|
||||
update(this.rootIssueStore.issues.issuesMap, [issueId, "module_ids"], (issueModuleIds = []) =>
|
||||
pull(issueModuleIds, moduleId)
|
||||
);
|
||||
});
|
||||
|
@ -14,21 +14,24 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface IProfileIssuesFilter {
|
||||
export interface IProfileIssuesFilter extends IBaseIssueFilterStore {
|
||||
// observables
|
||||
userId: string;
|
||||
filters: Record<string, IIssueFilters>; // Record defines userId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, userId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -96,6 +99,22 @@ export class ProfileIssuesFilter extends IssueFilterHelperStore implements IProf
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, userId: string) => {
|
||||
try {
|
||||
this.userId = userId;
|
||||
@ -150,12 +169,10 @@ export class ProfileIssuesFilter extends IssueFilterHelperStore implements IProf
|
||||
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.profileIssues.fetchIssues(
|
||||
this.rootIssueStore.profileIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
undefined,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation",
|
||||
userId,
|
||||
this.rootIssueStore.profileIssues.currentView
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
);
|
||||
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.PROFILE, type, workspaceSlug, userId, undefined, {
|
||||
@ -196,13 +213,7 @@ export class ProfileIssuesFilter extends IssueFilterHelperStore implements IProf
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.profileIssues.fetchIssues(
|
||||
workspaceSlug,
|
||||
undefined,
|
||||
"mutation",
|
||||
userId,
|
||||
this.rootIssueStore.profileIssues.currentView
|
||||
);
|
||||
this.rootIssueStore.profileIssues.fetchIssuesWithExistingPagination(workspaceSlug, userId, "mutation");
|
||||
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.PROFILE, type, workspaceSlug, userId, undefined, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,123 +1,63 @@
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
// base class
|
||||
import { UserService } from "services/user.service";
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TIssue, TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
// services
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { IProfileIssuesFilter } from "./filter.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
|
||||
interface IProfileIssueTabTypes {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface IProfileIssues {
|
||||
export interface IProfileIssues extends IBaseIssuesStore {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
currentView: "assigned" | "created" | "subscribed";
|
||||
issues: { [userId: string]: IProfileIssueTabTypes };
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
viewFlags: ViewFlags;
|
||||
// actions
|
||||
setViewId: (viewId: "assigned" | "created" | "subscribed") => void;
|
||||
// action
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
loadType: TLoader,
|
||||
userId: string,
|
||||
view?: "assigned" | "created" | "subscribed"
|
||||
) => Promise<TIssue[]>;
|
||||
createIssue: (
|
||||
loadType: TLoader,
|
||||
option: IssuePaginationOptions,
|
||||
view: "assigned" | "created" | "subscribed"
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, userId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, userId: string) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
userId: string,
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, userId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
export class ProfileIssues extends BaseIssuesStore implements IProfileIssues {
|
||||
currentView: "assigned" | "created" | "subscribed" = "assigned";
|
||||
issues: { [userId: string]: IProfileIssueTabTypes } = {};
|
||||
quickAddIssue = undefined;
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// filter store
|
||||
issueFilterStore: IProfileIssuesFilter;
|
||||
// services
|
||||
userService;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IProfileIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
currentView: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
viewFlags: computed,
|
||||
// action
|
||||
setViewId: action.bound,
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
// services
|
||||
this.userService = new UserService();
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const userId = this.rootIssueStore.userId;
|
||||
const workspaceSlug = this.rootIssueStore.workspaceSlug;
|
||||
const currentView = this.currentView;
|
||||
if (!userId || !currentView || !workspaceSlug) return undefined;
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${currentView}`;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.profileIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const userIssueIds = this.issues[userId]?.[uniqueViewId];
|
||||
|
||||
if (!userIssueIds) return;
|
||||
|
||||
const _issues = this.rootStore.issues.getIssuesByIds(userIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
get viewFlags() {
|
||||
if (this.currentView === "subscribed")
|
||||
return {
|
||||
@ -138,20 +78,20 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
loadType: TLoader = "init-loader",
|
||||
userId: string,
|
||||
view?: "assigned" | "created" | "subscribed"
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions,
|
||||
view: "assigned" | "created" | "subscribed"
|
||||
) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
if (view) this.currentView = view;
|
||||
});
|
||||
this.clear();
|
||||
|
||||
if (!this.currentView) throw new Error("current tab view is required");
|
||||
this.setViewId(view);
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${view}`;
|
||||
|
||||
let params: any = this.rootIssueStore?.profileIssuesFilter?.appliedFilters;
|
||||
let params = this.issueFilterStore?.getFilterParams(options);
|
||||
params = {
|
||||
...params,
|
||||
assignees: undefined,
|
||||
@ -164,17 +104,7 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
|
||||
const response = await this.userService.getUserProfileIssues(workspaceSlug, userId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[userId, uniqueViewId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
@ -182,73 +112,34 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, userId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, userId: string) => {
|
||||
if (!this.paginationOptions || !this.currentView) return;
|
||||
try {
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
this.loader = "pagination";
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||
let params = this.issueFilterStore?.getFilterParams(this.paginationOptions, this.nextCursor);
|
||||
params = {
|
||||
...params,
|
||||
assignees: undefined,
|
||||
created_by: undefined,
|
||||
subscriber: undefined,
|
||||
};
|
||||
if (this.currentView === "assigned") params = { ...params, assignees: userId };
|
||||
else if (this.currentView === "created") params = { ...params, created_by: userId };
|
||||
else if (this.currentView === "subscribed") params = { ...params, subscriber: userId };
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[userId][uniqueViewId].push(response.id);
|
||||
});
|
||||
|
||||
this.rootStore.issues.addIssue([response]);
|
||||
const response = await this.userService.getUserProfileIssues(workspaceSlug, userId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId: string
|
||||
) => {
|
||||
try {
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, data.id as keyof TIssue, data);
|
||||
} catch (error) {
|
||||
if (this.currentView) this.fetchIssues(workspaceSlug, undefined, "mutation", userId, this.currentView);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
userId: string | undefined = undefined
|
||||
) => {
|
||||
if (!userId) return;
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||
|
||||
const issueIndex = this.issues[userId][uniqueViewId].findIndex((_issueId) => _issueId === issueId);
|
||||
if (issueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[userId][uniqueViewId].splice(issueIndex, 1);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, userId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[userId][uniqueViewId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
fetchIssuesWithExistingPagination = async (workspaceSlug: string, userId: string, loadType: TLoader) => {
|
||||
if (!this.paginationOptions || !this.currentView) return;
|
||||
return await this.fetchIssues(workspaceSlug, userId, loadType, this.paginationOptions, this.currentView);
|
||||
};
|
||||
}
|
||||
|
@ -14,20 +14,22 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
export interface IProjectViewIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines viewId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface IProjectViewIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string, viewId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -93,6 +95,22 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string, viewId: string) => {
|
||||
try {
|
||||
const _filters = await this.issueFilterService.getViewDetails(workspaceSlug, projectId, viewId);
|
||||
@ -159,11 +177,10 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
|
||||
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.projectViewIssues.fetchIssues(
|
||||
this.rootIssueStore.projectViewIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation",
|
||||
viewId
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
);
|
||||
break;
|
||||
case EIssueFilterType.DISPLAY_FILTERS:
|
||||
@ -200,7 +217,11 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.projectViewIssues.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
this.rootIssueStore.projectViewIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
"mutation"
|
||||
);
|
||||
|
||||
await this.issueFilterService.patchView(workspaceSlug, projectId, viewId, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,230 +1,94 @@
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
import { action, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
import { IssueService } from "services/issue/issue.service";
|
||||
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { TIssue, TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse } from "@plane/types";
|
||||
// services
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
import { IProjectViewIssuesFilter } from "./filter.store";
|
||||
|
||||
export interface IProjectViewIssues {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [view_id: string]: string[] };
|
||||
export interface IProjectViewIssues extends IBaseIssuesStore {
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
viewId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
options: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
viewId?: string | undefined
|
||||
) => Promise<TIssue | undefined>;
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: TIssue) => Promise<TIssue | undefined>;
|
||||
removeBulkIssues: (workspaceSlug: string, projectId: string, issueIds: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ProjectViewIssues extends IssueHelperStore implements IProjectViewIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [view_id: string]: string[] } = {};
|
||||
export class ProjectViewIssues extends BaseIssuesStore implements IProjectViewIssues {
|
||||
viewFlags = {
|
||||
enableQuickAdd: true,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// services
|
||||
issueService;
|
||||
//filter store
|
||||
issueFilterStore: IProjectViewIssuesFilter;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IProjectViewIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
quickAddIssue: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
// services
|
||||
this.issueService = new IssueService();
|
||||
//filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const viewId = this.rootStore?.viewId;
|
||||
if (!viewId) return undefined;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.projectViewIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const viewIssueIds = this.issues[viewId];
|
||||
if (!viewIssueIds) return;
|
||||
|
||||
const _issues = this.rootStore.issues.getIssuesByIds(viewIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues = [];
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
} else if (layout === "calendar") issues = this.groupedIssues("target_date", "target_date", _issues, true);
|
||||
else if (layout === "spreadsheet") issues = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
else if (layout === "gantt_chart") issues = this.unGroupedIssues(orderBy ?? "sort_order", _issues);
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader", viewId: string) => {
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions
|
||||
) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.projectViewIssuesFilter?.appliedFilters;
|
||||
});
|
||||
this.clear();
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[viewId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, viewId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
this.loader = "pagination";
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[viewId].push(response.id);
|
||||
});
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
const issueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === issueId);
|
||||
if (issueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[viewId].splice(issueIndex, 1);
|
||||
});
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[viewId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: TIssue,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[viewId].push(data.id);
|
||||
this.rootIssueStore.issues.addIssue([data]);
|
||||
});
|
||||
|
||||
const response = await this.createIssue(workspaceSlug, projectId, data, viewId);
|
||||
|
||||
const quickAddIssueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === data.id);
|
||||
if (quickAddIssueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[viewId].splice(quickAddIssueIndex, 1);
|
||||
this.rootIssueStore.issues.removeIssue(data.id);
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (viewId) this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
fetchIssuesWithExistingPagination = async (workspaceSlug: string, projectId: string, loadType: TLoader) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions);
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import pickBy from "lodash/pickBy";
|
||||
import set from "lodash/set";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
// base class
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { handleIssueQueryParamsByLayout } from "helpers/issue.helper";
|
||||
import { IssueFiltersService } from "services/issue_filter.service";
|
||||
import {
|
||||
@ -14,20 +13,23 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType, IssueGroupByOptions } from "constants/issue";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// services
|
||||
|
||||
export interface IProjectIssuesFilter {
|
||||
// observables
|
||||
filters: Record<string, IIssueFilters>; // Record defines projectId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
export interface IProjectIssuesFilter extends IBaseIssueFilterStore {
|
||||
//helper actions
|
||||
getFilterParams: (
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
// action
|
||||
fetchFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -92,6 +94,22 @@ export class ProjectIssuesFilter extends IssueFilterHelperStore implements IProj
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
getFilterParams = computedFn((options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.appliedFilters;
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
try {
|
||||
const _filters = await this.issueFilterService.fetchProjectIssueFilters(workspaceSlug, projectId);
|
||||
@ -157,7 +175,7 @@ export class ProjectIssuesFilter extends IssueFilterHelperStore implements IProj
|
||||
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.projectIssues.fetchIssues(
|
||||
this.rootIssueStore.projectIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
@ -200,7 +218,7 @@ export class ProjectIssuesFilter extends IssueFilterHelperStore implements IProj
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.projectIssues.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.rootIssueStore.projectIssues.fetchIssuesWithExistingPagination(workspaceSlug, projectId, "mutation");
|
||||
|
||||
await this.issueFilterService.patchProjectIssueFilters(workspaceSlug, projectId, {
|
||||
display_filters: _filters.displayFilters,
|
||||
|
@ -1,123 +1,72 @@
|
||||
import concat from "lodash/concat";
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import update from "lodash/update";
|
||||
import { action, makeObservable, observable, runInAction, computed } from "mobx";
|
||||
// base class
|
||||
import { IssueService, IssueArchiveService } from "services/issue";
|
||||
import { TIssue, TGroupedIssues, TSubGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
// services
|
||||
// types
|
||||
import { action, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { TLoader, ViewFlags, IssuePaginationOptions, TIssuesResponse, TIssue } from "@plane/types";
|
||||
import { IProjectIssuesFilter } from "./filter.store";
|
||||
|
||||
export interface IProjectIssues {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: Record<string, string[]>; // Record of project_id as key and issue_ids as value
|
||||
export interface IProjectIssues extends IBaseIssuesStore {
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// action
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
option: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, projectId: string) => Promise<TIssuesResponse | undefined>;
|
||||
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: TIssue) => Promise<TIssue>;
|
||||
quickAddIssue: (workspaceSlug: string, projectId: string, data: TIssue) => Promise<TIssue | undefined>;
|
||||
removeBulkIssues: (workspaceSlug: string, projectId: string, issueIds: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
||||
// observable
|
||||
loader: TLoader = "init-loader";
|
||||
issues: Record<string, string[]> = {};
|
||||
export class ProjectIssues extends BaseIssuesStore implements IProjectIssues {
|
||||
viewFlags = {
|
||||
enableQuickAdd: true,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// services
|
||||
issueService;
|
||||
issueArchiveService;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
// filter store
|
||||
issueFilterStore: IProjectIssuesFilter;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IProjectIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
removeBulkIssues: action,
|
||||
quickAddIssue: action,
|
||||
fetchNextIssues: action,
|
||||
fetchIssuesWithExistingPagination: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
// services
|
||||
this.issueService = new IssueService();
|
||||
this.issueArchiveService = new IssueArchiveService();
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const projectId = this.rootStore?.projectId;
|
||||
if (!projectId) return undefined;
|
||||
|
||||
const displayFilters = this.rootStore?.projectIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
|
||||
const subGroupBy = displayFilters?.sub_group_by;
|
||||
const groupBy = displayFilters?.group_by;
|
||||
const orderBy = displayFilters?.order_by;
|
||||
const layout = displayFilters?.layout;
|
||||
|
||||
const projectIssueIds = this.issues[projectId];
|
||||
if (!projectIssueIds) return;
|
||||
|
||||
const _issues = this.rootStore.issues.getIssuesByIds(projectIssueIds, "un-archived");
|
||||
if (!_issues) return [];
|
||||
|
||||
let issues: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues = [];
|
||||
|
||||
if (layout === "list" && orderBy) {
|
||||
if (groupBy) issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
else issues = this.unGroupedIssues(orderBy, _issues);
|
||||
} else if (layout === "kanban" && groupBy && orderBy) {
|
||||
if (subGroupBy) issues = this.subGroupedIssues(subGroupBy, groupBy, orderBy, _issues);
|
||||
else issues = this.groupedIssues(groupBy, orderBy, _issues);
|
||||
} else if (layout === "calendar") issues = this.groupedIssues("target_date", "target_date", _issues, true);
|
||||
else if (layout === "spreadsheet") issues = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
else if (layout === "gantt_chart") issues = this.unGroupedIssues(orderBy ?? "sort_order", _issues);
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader") => {
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
options: IssuePaginationOptions
|
||||
) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootStore?.projectIssuesFilter?.appliedFilters;
|
||||
});
|
||||
this.clear();
|
||||
const params = this.issueFilterStore?.getFilterParams(options);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[projectId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
@ -125,102 +74,28 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, projectId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
||||
this.loader = "pagination";
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, [projectId], (issueIds) => {
|
||||
if (!issueIds) return [response.id];
|
||||
return concat(issueIds, response.id);
|
||||
});
|
||||
});
|
||||
|
||||
this.rootStore.issues.addIssue([response]);
|
||||
const params = this.issueFilterStore?.getFilterParams(this.paginationOptions);
|
||||
const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
try {
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
|
||||
await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[projectId], issueId);
|
||||
});
|
||||
|
||||
this.rootStore.issues.removeIssue(issueId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
try {
|
||||
const response = await this.issueArchiveService.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
this.rootStore.issues.updateIssue(issueId, {
|
||||
archived_at: response.archived_at,
|
||||
});
|
||||
pull(this.issues[projectId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
quickAddIssue = async (workspaceSlug: string, projectId: string, data: TIssue) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.issues[projectId].push(data.id);
|
||||
this.rootStore.issues.addIssue([data]);
|
||||
});
|
||||
|
||||
const response = await this.createIssue(workspaceSlug, projectId, data);
|
||||
|
||||
const quickAddIssueIndex = this.issues[projectId].findIndex((_issueId) => _issueId === data.id);
|
||||
if (quickAddIssueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[projectId].splice(quickAddIssueIndex, 1);
|
||||
this.rootStore.issues.removeIssue(data.id);
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeBulkIssues = async (workspaceSlug: string, projectId: string, issueIds: string[]) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
issueIds.forEach((issueId) => {
|
||||
pull(this.issues[projectId], issueId);
|
||||
this.rootStore.issues.removeIssue(issueId);
|
||||
});
|
||||
});
|
||||
|
||||
const response = await this.issueService.bulkDeleteIssues(workspaceSlug, projectId, { issue_ids: issueIds });
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
throw error;
|
||||
}
|
||||
fetchIssuesWithExistingPagination = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "mutation"
|
||||
) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions);
|
||||
};
|
||||
}
|
||||
|
@ -184,28 +184,28 @@ export class IssueRootStore implements IIssueRootStore {
|
||||
this.issueDetail = new IssueDetail(this);
|
||||
|
||||
this.workspaceIssuesFilter = new WorkspaceIssuesFilter(this);
|
||||
this.workspaceIssues = new WorkspaceIssues(this);
|
||||
this.workspaceIssues = new WorkspaceIssues(this, this.workspaceIssuesFilter);
|
||||
|
||||
this.profileIssuesFilter = new ProfileIssuesFilter(this);
|
||||
this.profileIssues = new ProfileIssues(this);
|
||||
this.profileIssues = new ProfileIssues(this, this.profileIssuesFilter);
|
||||
|
||||
this.projectIssuesFilter = new ProjectIssuesFilter(this);
|
||||
this.projectIssues = new ProjectIssues(this);
|
||||
this.projectIssues = new ProjectIssues(this, this.projectIssuesFilter);
|
||||
|
||||
this.cycleIssuesFilter = new CycleIssuesFilter(this);
|
||||
this.cycleIssues = new CycleIssues(this);
|
||||
this.cycleIssues = new CycleIssues(this, this.cycleIssuesFilter);
|
||||
|
||||
this.moduleIssuesFilter = new ModuleIssuesFilter(this);
|
||||
this.moduleIssues = new ModuleIssues(this);
|
||||
this.moduleIssues = new ModuleIssues(this, this.moduleIssuesFilter);
|
||||
|
||||
this.projectViewIssuesFilter = new ProjectViewIssuesFilter(this);
|
||||
this.projectViewIssues = new ProjectViewIssues(this);
|
||||
this.projectViewIssues = new ProjectViewIssues(this, this.projectViewIssuesFilter);
|
||||
|
||||
this.archivedIssuesFilter = new ArchivedIssuesFilter(this);
|
||||
this.archivedIssues = new ArchivedIssues(this);
|
||||
this.archivedIssues = new ArchivedIssues(this, this.archivedIssuesFilter);
|
||||
|
||||
this.draftIssuesFilter = new DraftIssuesFilter(this);
|
||||
this.draftIssues = new DraftIssues(this);
|
||||
this.draftIssues = new DraftIssues(this, this.draftIssuesFilter);
|
||||
|
||||
this.issueKanBanView = new IssueKanBanViewStore(this);
|
||||
this.issueCalendarView = new CalendarStore();
|
||||
|
@ -15,21 +15,19 @@ import {
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
TStaticViewTypes,
|
||||
IssuePaginationOptions,
|
||||
} from "@plane/types";
|
||||
import { IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
||||
// helpers
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// constants
|
||||
// services
|
||||
|
||||
type TWorkspaceFilters = "all-issues" | "assigned" | "created" | "subscribed" | string;
|
||||
export interface IWorkspaceIssuesFilter {
|
||||
// observables
|
||||
filters: Record<TWorkspaceFilters, IIssueFilters>; // Record defines viewId as key and IIssueFilters as value
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
|
||||
export interface IWorkspaceIssuesFilter extends IBaseIssueFilterStore {
|
||||
// fetch action
|
||||
fetchFilters: (workspaceSlug: string, viewId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
@ -42,6 +40,11 @@ export interface IWorkspaceIssuesFilter {
|
||||
//helper action
|
||||
getIssueFilters: (viewId: string | undefined) => IIssueFilters | undefined;
|
||||
getAppliedFilters: (viewId: string) => Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
getFilterParams: (
|
||||
viewId: string,
|
||||
options: IssuePaginationOptions,
|
||||
cursor?: string
|
||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
||||
}
|
||||
|
||||
export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWorkspaceIssuesFilter {
|
||||
@ -63,9 +66,6 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
// fetch actions
|
||||
fetchFilters: action,
|
||||
updateFilters: action,
|
||||
// helper actions
|
||||
getIssueFilters: action,
|
||||
getAppliedFilters: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
@ -102,6 +102,22 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
return filteredRouteParams;
|
||||
};
|
||||
|
||||
getFilterParams = computedFn((viewId: string, options: IssuePaginationOptions, cursor: string | undefined) => {
|
||||
const filterParams = this.getAppliedFilters(viewId);
|
||||
|
||||
const paginationOptions: Partial<Record<TIssueParams, string | boolean>> = {
|
||||
...filterParams,
|
||||
cursor: cursor ? cursor : `${options.perPageCount}:0:0`,
|
||||
per_page: options.perPageCount.toString(),
|
||||
};
|
||||
|
||||
if (options.groupedBy) {
|
||||
paginationOptions.group_by = options.groupedBy;
|
||||
}
|
||||
|
||||
return paginationOptions;
|
||||
});
|
||||
|
||||
get issueFilters() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
return this.getIssueFilters(viewId);
|
||||
@ -182,7 +198,7 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
});
|
||||
const appliedFilters = _filters.filters || {};
|
||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
||||
this.rootIssueStore.workspaceIssues.fetchIssues(
|
||||
this.rootIssueStore.workspaceIssues.fetchIssuesWithExistingPagination(
|
||||
workspaceSlug,
|
||||
viewId,
|
||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
||||
@ -222,7 +238,7 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
});
|
||||
|
||||
if (this.requiresServerUpdate(updatedDisplayFilters))
|
||||
this.rootIssueStore.workspaceIssues.fetchIssues(workspaceSlug, viewId, "mutation");
|
||||
this.rootIssueStore.workspaceIssues.fetchIssuesWithExistingPagination(workspaceSlug, viewId, "mutation");
|
||||
|
||||
if (["all-issues", "assigned", "created", "subscribed"].includes(viewId))
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.GLOBAL, type, workspaceSlug, undefined, viewId, {
|
||||
|
@ -1,215 +1,93 @@
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||
import { action, makeObservable, runInAction } from "mobx";
|
||||
// base class
|
||||
import { IssueService, IssueArchiveService } from "services/issue";
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
import { TIssue, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||
import { IssuePaginationOptions, TIssue, TIssuesResponse, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
|
||||
// services
|
||||
// types
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
||||
import { IWorkspaceIssuesFilter } from "./filter.store";
|
||||
|
||||
export interface IWorkspaceIssues {
|
||||
export interface IWorkspaceIssues extends IBaseIssuesStore {
|
||||
// observable
|
||||
loader: TLoader;
|
||||
issues: { [viewId: string]: string[] };
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: { dataViewId: string; issueIds: TUnGroupedIssues | undefined };
|
||||
// actions
|
||||
fetchIssues: (workspaceSlug: string, viewId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
createIssue: (
|
||||
fetchIssues: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
viewId: string,
|
||||
loadType: TLoader,
|
||||
options: IssuePaginationOptions
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchIssuesWithExistingPagination: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
viewId: string,
|
||||
loadType: TLoader
|
||||
) => Promise<TIssuesResponse | undefined>;
|
||||
fetchNextIssues: (workspaceSlug: string, viewId: string) => Promise<TIssuesResponse | undefined>;
|
||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssues {
|
||||
loader: TLoader = "init-loader";
|
||||
issues: { [viewId: string]: string[] } = {};
|
||||
export class WorkspaceIssues extends BaseIssuesStore implements IWorkspaceIssues {
|
||||
viewFlags = {
|
||||
enableQuickAdd: true,
|
||||
enableIssueCreation: true,
|
||||
enableInlineEditing: true,
|
||||
};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
// service
|
||||
workspaceService;
|
||||
issueService;
|
||||
issueArchiveService;
|
||||
// filterStore
|
||||
issueFilterStore;
|
||||
|
||||
quickAddIssue = undefined;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IWorkspaceIssuesFilter) {
|
||||
super(_rootStore, issueFilterStore);
|
||||
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
fetchIssues: action,
|
||||
createIssue: action,
|
||||
updateIssue: action,
|
||||
removeIssue: action,
|
||||
archiveIssue: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
// services
|
||||
this.workspaceService = new WorkspaceService();
|
||||
this.issueService = new IssueService();
|
||||
this.issueArchiveService = new IssueArchiveService();
|
||||
// filter store
|
||||
this.issueFilterStore = issueFilterStore;
|
||||
}
|
||||
|
||||
get groupedIssueIds() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
const workspaceSlug = this.rootIssueStore.workspaceSlug;
|
||||
if (!workspaceSlug || !viewId) return { dataViewId: "", issueIds: undefined };
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
const displayFilters = this.rootIssueStore?.workspaceIssuesFilter?.filters?.[viewId]?.displayFilters;
|
||||
if (!displayFilters) return { dataViewId: viewId, issueIds: undefined };
|
||||
|
||||
const orderBy = displayFilters?.order_by;
|
||||
|
||||
const viewIssueIds = this.issues[uniqueViewId];
|
||||
|
||||
if (!viewIssueIds) return { dataViewId: viewId, issueIds: undefined };
|
||||
|
||||
const _issues = this.rootStore.issues.getIssuesByIds(viewIssueIds, "un-archived");
|
||||
if (!_issues) return { dataViewId: viewId, issueIds: [] };
|
||||
|
||||
let issueIds: TIssue | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
issueIds = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
|
||||
return { dataViewId: viewId, issueIds };
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, viewId: string, loadType: TLoader = "init-loader") => {
|
||||
fetchIssues = async (workspaceSlug: string, viewId: string, loadType: TLoader, options: IssuePaginationOptions) => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.loader = loadType;
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
const params = this.rootIssueStore?.workspaceIssuesFilter?.getAppliedFilters(viewId);
|
||||
});
|
||||
this.clear();
|
||||
const params = this.issueFilterStore?.getFilterParams(viewId, options, undefined);
|
||||
const response = await this.workspaceService.getViewIssues(workspaceSlug, params);
|
||||
|
||||
runInAction(() => {
|
||||
set(
|
||||
this.issues,
|
||||
[uniqueViewId],
|
||||
response.map((issue) => issue.id)
|
||||
);
|
||||
this.loader = undefined;
|
||||
});
|
||||
|
||||
this.rootIssueStore.issues.addIssue(response);
|
||||
|
||||
this.onfetchIssues(response, options);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, viewId: string) => {
|
||||
fetchNextIssues = async (workspaceSlug: string, viewId: string) => {
|
||||
if (!this.paginationOptions) return;
|
||||
try {
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
this.loader = "pagination";
|
||||
|
||||
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
||||
|
||||
runInAction(() => {
|
||||
this.issues[uniqueViewId].push(response.id);
|
||||
});
|
||||
|
||||
this.rootStore.issues.addIssue([response]);
|
||||
const params = this.issueFilterStore?.getFilterParams(viewId, this.paginationOptions, this.nextCursor);
|
||||
const response = await this.workspaceService.getViewIssues(workspaceSlug, params);
|
||||
|
||||
this.onfetchNexIssues(response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
if (viewId) this.fetchIssues(workspaceSlug, viewId, "mutation");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
const issueIndex = this.issues[uniqueViewId].findIndex((_issueId) => _issueId === issueId);
|
||||
if (issueIndex >= 0)
|
||||
runInAction(() => {
|
||||
this.issues[uniqueViewId].splice(issueIndex, 1);
|
||||
});
|
||||
|
||||
this.rootStore.issues.removeIssue(issueId);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
const response = await this.issueArchiveService.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
this.rootStore.issues.updateIssue(issueId, {
|
||||
archived_at: response.archived_at,
|
||||
});
|
||||
pull(this.issues[uniqueViewId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
fetchIssuesWithExistingPagination = async (workspaceSlug: string, viewId: string, loadType: TLoader) => {
|
||||
if (!this.paginationOptions) return;
|
||||
return await this.fetchIssues(workspaceSlug, viewId, loadType, this.paginationOptions);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user