ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: стабилизация read-only фильтров двусторонней доски внешних контуров
This commit is contained in:
@@ -8,6 +8,7 @@ import { API_BASE_URL } from "@plane/constants";
|
||||
import type {
|
||||
TExternalContourBoardFilter,
|
||||
TExternalContourBoardResponse,
|
||||
TExternalContourBoardSorting,
|
||||
TExternalContourRequest,
|
||||
TExternalContourRequestResponse,
|
||||
TExternalContourTargetOptions,
|
||||
@@ -32,10 +33,11 @@ export class ExternalContourService extends APIService {
|
||||
async listBoard(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
filters: Partial<TExternalContourBoardFilter> = {}
|
||||
filters: Partial<TExternalContourBoardFilter> = {},
|
||||
sorting: TExternalContourBoardSorting = {}
|
||||
): Promise<TExternalContourBoardResponse> {
|
||||
const params = Object.fromEntries(
|
||||
Object.entries(filters).flatMap(([key, value]) => {
|
||||
Object.entries({ ...filters, ...sorting }).flatMap(([key, value]) => {
|
||||
if (value === undefined || value === null || value === "") return [];
|
||||
if (Array.isArray(value)) return [[key, value.join(",")]];
|
||||
return [[key, String(value)]];
|
||||
|
||||
+100
-16
@@ -16,7 +16,18 @@ import { EInboxIssueCurrentTab } from "@plane/types";
|
||||
import { ExternalContourService } from "@/services/external-contours";
|
||||
import type { CoreRootStore } from "../root.store";
|
||||
|
||||
type TLoader = "init-loading" | undefined;
|
||||
type TLoader = "init-loading" | "loading" | undefined;
|
||||
|
||||
const DEFAULT_SORTING: TExternalContourBoardSorting = { order_by: "updated_at", sort_by: "desc" };
|
||||
|
||||
const sanitizeBoardFilters = (filters: Partial<TExternalContourBoardFilter>): Partial<TExternalContourBoardFilter> =>
|
||||
Object.fromEntries(
|
||||
Object.entries(filters).flatMap(([key, value]) => {
|
||||
if (value === undefined || value === null || value === "") return [];
|
||||
if (Array.isArray(value) && value.length === 0) return [];
|
||||
return [[key, value]];
|
||||
})
|
||||
) as Partial<TExternalContourBoardFilter>;
|
||||
|
||||
export interface IProjectExternalContoursBoardStore {
|
||||
currentProjectId: string;
|
||||
@@ -29,12 +40,18 @@ export interface IProjectExternalContoursBoardStore {
|
||||
columnIdsMap: Record<TExternalContourBoardDirection, string[]>;
|
||||
columnCountMap: Record<TExternalContourBoardDirection, number>;
|
||||
tabCountMap: Record<TInboxIssueCurrentTab, number>;
|
||||
activeFiltersCount: number;
|
||||
fetchBoard: (workspaceSlug: string, projectId: string, tab?: TInboxIssueCurrentTab) => Promise<void>;
|
||||
getColumnRequestIds: (direction: TExternalContourBoardDirection) => string[];
|
||||
getColumnTotalCount: (direction: TExternalContourBoardDirection) => number;
|
||||
getRequestById: (requestId: string) => TExternalContourRequest | undefined;
|
||||
handleCurrentTab: (workspaceSlug: string, projectId: string, tab: TInboxIssueCurrentTab) => Promise<void>;
|
||||
hasAnyItems: boolean;
|
||||
isFiltering: boolean;
|
||||
isSortingDefault: boolean;
|
||||
clearFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
updateFilters: (workspaceSlug: string, projectId: string, filters: Partial<TExternalContourBoardFilter>) => Promise<void>;
|
||||
updateSorting: (workspaceSlug: string, projectId: string, sorting: TExternalContourBoardSorting) => Promise<void>;
|
||||
upsertBoardItems: (items: TExternalContourRequest[]) => void;
|
||||
}
|
||||
|
||||
@@ -45,7 +62,7 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
filters: Partial<TExternalContourBoardFilter> = { status: [EInboxIssueCurrentTab.OPEN] };
|
||||
items: Record<string, TExternalContourRequest> = {};
|
||||
loader: TLoader = "init-loading";
|
||||
sorting: TExternalContourBoardSorting = { order_by: "updated_at", sort_by: "desc" };
|
||||
sorting: TExternalContourBoardSorting = DEFAULT_SORTING;
|
||||
columnIdsMap: Record<TExternalContourBoardDirection, string[]> = {
|
||||
outgoing: [],
|
||||
incoming: [],
|
||||
@@ -58,6 +75,8 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
[EInboxIssueCurrentTab.OPEN]: 0,
|
||||
[EInboxIssueCurrentTab.CLOSED]: 0,
|
||||
};
|
||||
hydratedProjectId = "";
|
||||
lastIssuedRequestId = 0;
|
||||
|
||||
externalContourService;
|
||||
|
||||
@@ -73,9 +92,15 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
columnIdsMap: observable,
|
||||
columnCountMap: observable,
|
||||
tabCountMap: observable,
|
||||
activeFiltersCount: computed,
|
||||
hasAnyItems: computed,
|
||||
isFiltering: computed,
|
||||
isSortingDefault: computed,
|
||||
clearFilters: action,
|
||||
fetchBoard: action,
|
||||
handleCurrentTab: action,
|
||||
updateFilters: action,
|
||||
updateSorting: action,
|
||||
upsertBoardItems: action,
|
||||
});
|
||||
|
||||
@@ -86,6 +111,24 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
return this.columnIdsMap.outgoing.length > 0 || this.columnIdsMap.incoming.length > 0;
|
||||
}
|
||||
|
||||
get isFiltering() {
|
||||
return this.loader === "loading";
|
||||
}
|
||||
|
||||
get isSortingDefault() {
|
||||
return this.sorting.order_by === DEFAULT_SORTING.order_by && this.sorting.sort_by === DEFAULT_SORTING.sort_by;
|
||||
}
|
||||
|
||||
get activeFiltersCount() {
|
||||
return Object.entries(this.filters).reduce((count, [key, value]) => {
|
||||
if (key === "status") return count;
|
||||
if (value === undefined || value === null || value === "") return count;
|
||||
if (Array.isArray(value)) return count + (value.length > 0 ? 1 : 0);
|
||||
if (typeof value === "boolean") return count + (value ? 1 : 0);
|
||||
return count + 1;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
getRequestById = (requestId: string) => this.items[requestId];
|
||||
|
||||
getColumnRequestIds = (direction: TExternalContourBoardDirection) => this.columnIdsMap[direction] ?? [];
|
||||
@@ -101,36 +144,75 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
};
|
||||
|
||||
handleCurrentTab = async (workspaceSlug: string, projectId: string, tab: TInboxIssueCurrentTab) => {
|
||||
this.currentProjectId = projectId;
|
||||
this.currentTab = tab;
|
||||
this.filters = {
|
||||
this.filters = sanitizeBoardFilters({
|
||||
...this.filters,
|
||||
status: [tab],
|
||||
};
|
||||
});
|
||||
await this.fetchBoard(workspaceSlug, projectId, tab);
|
||||
};
|
||||
|
||||
updateFilters = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
filters: Partial<TExternalContourBoardFilter>
|
||||
) => {
|
||||
this.filters = sanitizeBoardFilters({
|
||||
...this.filters,
|
||||
...filters,
|
||||
status: [this.currentTab],
|
||||
});
|
||||
|
||||
await this.fetchBoard(workspaceSlug, projectId, this.currentTab);
|
||||
};
|
||||
|
||||
updateSorting = async (workspaceSlug: string, projectId: string, sorting: TExternalContourBoardSorting) => {
|
||||
this.sorting = sorting;
|
||||
await this.fetchBoard(workspaceSlug, projectId, this.currentTab);
|
||||
};
|
||||
|
||||
clearFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
this.filters = { status: [this.currentTab] };
|
||||
this.sorting = DEFAULT_SORTING;
|
||||
await this.fetchBoard(workspaceSlug, projectId, this.currentTab);
|
||||
};
|
||||
|
||||
fetchBoard = async (workspaceSlug: string, projectId: string, tab = this.currentTab) => {
|
||||
this.loader = "init-loading";
|
||||
const hasProjectChanged = !!this.currentProjectId && this.currentProjectId !== projectId;
|
||||
const isInitialLoad = this.hydratedProjectId !== projectId;
|
||||
const nextFilters = sanitizeBoardFilters({
|
||||
...(hasProjectChanged ? {} : this.filters),
|
||||
status: [tab],
|
||||
});
|
||||
const nextSorting = hasProjectChanged ? DEFAULT_SORTING : this.sorting;
|
||||
const requestId = ++this.lastIssuedRequestId;
|
||||
|
||||
this.loader = isInitialLoad ? "init-loading" : "loading";
|
||||
this.error = undefined;
|
||||
if (hasProjectChanged) {
|
||||
this.items = {};
|
||||
this.columnIdsMap = { outgoing: [], incoming: [] };
|
||||
this.columnCountMap = { outgoing: 0, incoming: 0 };
|
||||
this.tabCountMap = {
|
||||
[EInboxIssueCurrentTab.OPEN]: 0,
|
||||
[EInboxIssueCurrentTab.CLOSED]: 0,
|
||||
};
|
||||
}
|
||||
this.currentProjectId = projectId;
|
||||
this.currentTab = tab;
|
||||
this.filters = {
|
||||
...this.filters,
|
||||
status: [tab],
|
||||
};
|
||||
this.filters = nextFilters;
|
||||
this.sorting = nextSorting;
|
||||
|
||||
try {
|
||||
const response = await this.externalContourService.listBoard(workspaceSlug, projectId, {
|
||||
status: tab,
|
||||
});
|
||||
const response = await this.externalContourService.listBoard(workspaceSlug, projectId, nextFilters, nextSorting);
|
||||
if (requestId !== this.lastIssuedRequestId) return;
|
||||
|
||||
runInAction(() => {
|
||||
this.items = {};
|
||||
this.columnIdsMap = { outgoing: [], incoming: [] };
|
||||
this.columnCountMap = { outgoing: 0, incoming: 0 };
|
||||
this.filters = response.filters || { status: [tab] };
|
||||
this.sorting = response.sorting || { order_by: "updated_at", sort_by: "desc" };
|
||||
this.filters = sanitizeBoardFilters(response.filters || nextFilters);
|
||||
this.sorting = response.sorting || nextSorting;
|
||||
this.hydratedProjectId = projectId;
|
||||
|
||||
response.columns.forEach((column) => {
|
||||
this.columnIdsMap[column.key] = column.results.map((request) => request.id);
|
||||
@@ -146,6 +228,8 @@ export class ProjectExternalContoursBoardStore implements IProjectExternalContou
|
||||
this.loader = undefined;
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (requestId !== this.lastIssuedRequestId) return;
|
||||
|
||||
runInAction(() => {
|
||||
this.loader = undefined;
|
||||
this.error = {
|
||||
|
||||
Reference in New Issue
Block a user