[WEB-4321]chore: workspace views refactor (#7214)
* chore: workspace views reafactor * chore: resolved coderabbit suggestions * chore: added project level workspace filter * chore: added enum for roles * chore: removed redundant type definition * chore: optimised the query --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
parent
8988cf9a85
commit
64fd0b2830
24 changed files with 381 additions and 172 deletions
|
|
@ -944,9 +944,33 @@ class IssueDetailEndpoint(BaseAPIView):
|
|||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||
def get(self, request, slug, project_id):
|
||||
filters = issue_filters(request.query_params, "GET")
|
||||
|
||||
# check for the project member role, if the role is 5 then check for the guest_view_all_features
|
||||
# if it is true then show all the issues else show only the issues created by the user
|
||||
project_member_subquery = ProjectMember.objects.filter(
|
||||
project_id=OuterRef("project_id"),
|
||||
member=self.request.user,
|
||||
is_active=True,
|
||||
).filter(
|
||||
Q(role__gt=ROLE.GUEST.value)
|
||||
| Q(
|
||||
role=ROLE.GUEST.value, project__guest_view_all_features=True
|
||||
)
|
||||
)
|
||||
|
||||
# Main issue query
|
||||
issue = (
|
||||
Issue.issue_objects.filter(workspace__slug=slug, project_id=project_id)
|
||||
.select_related("workspace", "project", "state", "parent")
|
||||
.filter(
|
||||
Q(Exists(project_member_subquery))
|
||||
| Q(
|
||||
project__project_projectmember__member=self.request.user,
|
||||
project__project_projectmember__is_active=True,
|
||||
project__project_projectmember__role=ROLE.GUEST.value,
|
||||
project__guest_view_all_features=False,
|
||||
created_by=self.request.user,
|
||||
)
|
||||
)
|
||||
.prefetch_related("assignees", "labels", "issue_module__module")
|
||||
.annotate(
|
||||
cycle_id=Subquery(
|
||||
|
|
@ -1014,6 +1038,7 @@ class IssueDetailEndpoint(BaseAPIView):
|
|||
.values("count")
|
||||
)
|
||||
)
|
||||
|
||||
issue = issue.filter(**filters)
|
||||
order_by_param = request.GET.get("order_by", "-created_at")
|
||||
# Issue queryset
|
||||
|
|
|
|||
2
packages/types/src/layout/gantt.d.ts
vendored
2
packages/types/src/layout/gantt.d.ts
vendored
|
|
@ -9,6 +9,7 @@ export interface IGanttBlock {
|
|||
sort_order: number | undefined;
|
||||
start_date: string | undefined;
|
||||
target_date: string | undefined;
|
||||
project_id: string | undefined;
|
||||
}
|
||||
|
||||
export interface IBlockUpdateData {
|
||||
|
|
@ -25,6 +26,7 @@ export interface IBlockUpdateDependencyData {
|
|||
id: string;
|
||||
start_date?: string;
|
||||
target_date?: string;
|
||||
project_id?: string;
|
||||
}
|
||||
|
||||
export type TGanttViews = "week" | "month" | "quarter";
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ export const getIssueBlocksStructure = (block: TIssue): IGanttBlock => ({
|
|||
sort_order: block?.sort_order,
|
||||
start_date: block?.start_date ?? undefined,
|
||||
target_date: block?.target_date ?? undefined,
|
||||
project_id: block?.project_id ?? undefined,
|
||||
});
|
||||
|
||||
export const formatTextList = (TextArray: string[]): string => {
|
||||
|
|
@ -260,7 +261,7 @@ export const getComputedDisplayFilters = (
|
|||
displayFilters: IIssueDisplayFilterOptions = {},
|
||||
defaultValues?: IIssueDisplayFilterOptions
|
||||
): IIssueDisplayFilterOptions => {
|
||||
const filters = displayFilters || defaultValues;
|
||||
const filters = !isEmpty(displayFilters) ? displayFilters : defaultValues;
|
||||
|
||||
return {
|
||||
calendar: {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Layers } from "lucide-react";
|
||||
// plane constants
|
||||
import { EIssueFilterType, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// types
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types";
|
||||
|
|
@ -19,6 +19,7 @@ import { CreateUpdateWorkspaceViewModal } from "@/components/workspace";
|
|||
// helpers
|
||||
// hooks
|
||||
import { useLabel, useMember, useIssues, useGlobalView } from "@/hooks/store";
|
||||
import { GlobalViewLayoutSelection } from "@/plane-web/components/views/helper";
|
||||
|
||||
export const GlobalIssuesHeader = observer(() => {
|
||||
// states
|
||||
|
|
@ -38,6 +39,7 @@ export const GlobalIssuesHeader = observer(() => {
|
|||
|
||||
const issueFilters = globalViewId ? filters[globalViewId.toString()] : undefined;
|
||||
|
||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||
const viewDetails = getViewDetailsById(globalViewId.toString());
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
|
|
@ -95,8 +97,27 @@ export const GlobalIssuesHeader = observer(() => {
|
|||
[workspaceSlug, updateFilters, globalViewId]
|
||||
);
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: EIssueLayoutTypes) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
undefined,
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ layout: layout },
|
||||
globalViewId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, updateFilters, globalViewId]
|
||||
);
|
||||
|
||||
const isLocked = viewDetails?.is_locked;
|
||||
|
||||
const currentLayoutFilters = useMemo(() => {
|
||||
const layout = activeLayout ?? EIssueLayoutTypes.SPREADSHEET;
|
||||
return ISSUE_DISPLAY_FILTERS_BY_PAGE.my_issues[layout];
|
||||
}, [activeLayout]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
||||
|
|
@ -113,13 +134,18 @@ export const GlobalIssuesHeader = observer(() => {
|
|||
<Header.RightItem>
|
||||
{!isLocked ? (
|
||||
<>
|
||||
<GlobalViewLayoutSelection
|
||||
onChange={handleLayoutChange}
|
||||
selectedLayout={activeLayout ?? EIssueLayoutTypes.SPREADSHEET}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
/>
|
||||
<FiltersDropdown
|
||||
title={t("common.filters")}
|
||||
placement="bottom-end"
|
||||
isFiltersApplied={isIssueFilterActive(issueFilters)}
|
||||
>
|
||||
<FilterSelection
|
||||
layoutDisplayFiltersOptions={ISSUE_DISPLAY_FILTERS_BY_PAGE.my_issues.spreadsheet}
|
||||
layoutDisplayFiltersOptions={currentLayoutFilters}
|
||||
filters={issueFilters?.filters ?? {}}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
|
|
@ -130,7 +156,7 @@ export const GlobalIssuesHeader = observer(() => {
|
|||
</FiltersDropdown>
|
||||
<FiltersDropdown title={t("common.display")} placement="bottom-end">
|
||||
<DisplayFiltersSelection
|
||||
layoutDisplayFiltersOptions={ISSUE_DISPLAY_FILTERS_BY_PAGE.my_issues.spreadsheet}
|
||||
layoutDisplayFiltersOptions={currentLayoutFilters}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
|
|
|
|||
12
web/ce/components/views/helper.tsx
Normal file
12
web/ce/components/views/helper.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { EIssueLayoutTypes } from "@plane/constants";
|
||||
import { TWorkspaceLayoutProps } from "@/components/views/helper";
|
||||
|
||||
export type TLayoutSelectionProps = {
|
||||
onChange: (layout: EIssueLayoutTypes) => void;
|
||||
selectedLayout: EIssueLayoutTypes;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
export const GlobalViewLayoutSelection = (props: TLayoutSelectionProps) => <></>;
|
||||
|
||||
export const WorkspaceAdditionalLayouts = (props: TWorkspaceLayoutProps) => <></>;
|
||||
1
web/ce/store/issue/workspace/issue.store.ts
Normal file
1
web/ce/store/issue/workspace/issue.store.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "@/store/issue/workspace/issue.store";
|
||||
|
|
@ -22,6 +22,7 @@ type BlockData = {
|
|||
sort_order: number | null;
|
||||
start_date?: string | undefined | null;
|
||||
target_date?: string | undefined | null;
|
||||
project_id?: string | undefined | null;
|
||||
};
|
||||
|
||||
export interface IBaseTimelineStore {
|
||||
|
|
@ -194,6 +195,7 @@ export class BaseTimeLineStore implements IBaseTimelineStore {
|
|||
sort_order: blockData?.sort_order ?? undefined,
|
||||
start_date: blockData?.start_date ?? undefined,
|
||||
target_date: blockData?.target_date ?? undefined,
|
||||
project_id: blockData?.project_id ?? undefined,
|
||||
};
|
||||
if (this.currentViewData && (this.currentViewData?.data?.startDate || this.currentViewData?.data?.dayWidth)) {
|
||||
block.position = getItemPositionWidth(this.currentViewData, block);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type Props = {
|
|||
enableBlockLeftResize: boolean;
|
||||
enableBlockRightResize: boolean;
|
||||
enableBlockMove: boolean;
|
||||
enableDependency: boolean;
|
||||
ganttContainerRef: RefObject<HTMLDivElement>;
|
||||
updateBlockDates?: (updates: IBlockUpdateDependencyData[]) => Promise<void>;
|
||||
};
|
||||
|
|
@ -33,6 +34,7 @@ export const GanttChartBlock: React.FC<Props> = observer((props) => {
|
|||
enableBlockRightResize,
|
||||
enableBlockMove,
|
||||
ganttContainerRef,
|
||||
enableDependency,
|
||||
updateBlockDates,
|
||||
} = props;
|
||||
// store hooks
|
||||
|
|
@ -90,6 +92,7 @@ export const GanttChartBlock: React.FC<Props> = observer((props) => {
|
|||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove && !!isBlockComplete}
|
||||
enableDependency={enableDependency}
|
||||
isMoving={isMoving}
|
||||
ganttContainerRef={ganttContainerRef}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export type GanttChartBlocksProps = {
|
|||
ganttContainerRef: React.RefObject<HTMLDivElement>;
|
||||
showAllBlocks: boolean;
|
||||
updateBlockDates?: (updates: IBlockUpdateDependencyData[]) => Promise<void>;
|
||||
enableDependency: boolean | ((blockId: string) => boolean);
|
||||
};
|
||||
|
||||
export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
||||
|
|
@ -24,6 +25,7 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
|||
ganttContainerRef,
|
||||
showAllBlocks,
|
||||
updateBlockDates,
|
||||
enableDependency,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
|
@ -41,6 +43,7 @@ export const GanttChartBlocksList: FC<GanttChartBlocksProps> = (props) => {
|
|||
typeof enableBlockRightResize === "function" ? enableBlockRightResize(blockId) : enableBlockRightResize
|
||||
}
|
||||
enableBlockMove={typeof enableBlockMove === "function" ? enableBlockMove(blockId) : enableBlockMove}
|
||||
enableDependency={typeof enableDependency === "function" ? enableDependency(blockId) : enableDependency}
|
||||
ganttContainerRef={ganttContainerRef}
|
||||
updateBlockDates={updateBlockDates}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ type Props = {
|
|||
enableReorder: boolean | ((blockId: string) => boolean);
|
||||
enableSelection: boolean | ((blockId: string) => boolean);
|
||||
enableAddBlock: boolean | ((blockId: string) => boolean);
|
||||
enableDependency: boolean | ((blockId: string) => boolean);
|
||||
itemsContainerWidth: number;
|
||||
showAllBlocks: boolean;
|
||||
sidebarToRender: (props: any) => React.ReactNode;
|
||||
|
|
@ -67,6 +68,7 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
|
|||
enableReorder,
|
||||
enableAddBlock,
|
||||
enableSelection,
|
||||
enableDependency,
|
||||
itemsContainerWidth,
|
||||
showAllBlocks,
|
||||
sidebarToRender,
|
||||
|
|
@ -215,6 +217,7 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
|
|||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
ganttContainerRef={ganttContainerRef}
|
||||
enableDependency={enableDependency}
|
||||
showAllBlocks={showAllBlocks}
|
||||
updateBlockDates={updateBlockDates}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type ChartViewRootProps = {
|
|||
enableReorder: boolean | ((blockId: string) => boolean);
|
||||
enableAddBlock: boolean | ((blockId: string) => boolean);
|
||||
enableSelection: boolean | ((blockId: string) => boolean);
|
||||
enableDependency: boolean | ((blockId: string) => boolean);
|
||||
bottomSpacing: boolean;
|
||||
showAllBlocks: boolean;
|
||||
loadMoreBlocks?: () => void;
|
||||
|
|
@ -70,6 +71,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
|
|||
enableReorder,
|
||||
enableAddBlock,
|
||||
enableSelection,
|
||||
enableDependency,
|
||||
bottomSpacing,
|
||||
showAllBlocks,
|
||||
quickAdd,
|
||||
|
|
@ -204,6 +206,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
|
|||
enableReorder={enableReorder}
|
||||
enableSelection={enableSelection}
|
||||
enableAddBlock={enableAddBlock}
|
||||
enableDependency={enableDependency}
|
||||
itemsContainerWidth={itemsContainerWidth}
|
||||
showAllBlocks={showAllBlocks}
|
||||
sidebarToRender={sidebarToRender}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ type Props = {
|
|||
enableBlockLeftResize: boolean;
|
||||
enableBlockRightResize: boolean;
|
||||
enableBlockMove: boolean;
|
||||
enableDependency: boolean | ((blockId: string) => boolean);
|
||||
ganttContainerRef: RefObject<HTMLDivElement>;
|
||||
};
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ export const ChartDraggable: React.FC<Props> = observer((props) => {
|
|||
enableBlockLeftResize,
|
||||
enableBlockRightResize,
|
||||
enableBlockMove,
|
||||
enableDependency,
|
||||
isMoving,
|
||||
ganttContainerRef,
|
||||
} = props;
|
||||
|
|
@ -36,7 +38,9 @@ export const ChartDraggable: React.FC<Props> = observer((props) => {
|
|||
return (
|
||||
<div className="group w-full z-[5] relative inline-flex h-full cursor-pointer items-center font-medium transition-all">
|
||||
{/* left resize drag handle */}
|
||||
<LeftDependencyDraggable block={block} ganttContainerRef={ganttContainerRef} />
|
||||
{(typeof enableDependency === "function" ? enableDependency(block.id) : enableDependency) && (
|
||||
<LeftDependencyDraggable block={block} ganttContainerRef={ganttContainerRef} />
|
||||
)}
|
||||
<LeftResizable
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
handleBlockDrag={handleBlockDrag}
|
||||
|
|
@ -58,7 +62,9 @@ export const ChartDraggable: React.FC<Props> = observer((props) => {
|
|||
isMoving={isMoving}
|
||||
position={block.position}
|
||||
/>
|
||||
<RightDependencyDraggable block={block} ganttContainerRef={ganttContainerRef} />
|
||||
{(typeof enableDependency === "function" ? enableDependency(block.id) : enableDependency) && (
|
||||
<RightDependencyDraggable block={block} ganttContainerRef={ganttContainerRef} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ type GanttChartRootProps = {
|
|||
enableReorder?: boolean | ((blockId: string) => boolean);
|
||||
enableAddBlock?: boolean | ((blockId: string) => boolean);
|
||||
enableSelection?: boolean | ((blockId: string) => boolean);
|
||||
enableDependency?: boolean | ((blockId: string) => boolean);
|
||||
bottomSpacing?: boolean;
|
||||
showAllBlocks?: boolean;
|
||||
showToday?: boolean;
|
||||
|
|
@ -47,6 +48,7 @@ export const GanttChartRoot: FC<GanttChartRootProps> = observer((props) => {
|
|||
enableReorder = false,
|
||||
enableAddBlock = false,
|
||||
enableSelection = false,
|
||||
enableDependency = false,
|
||||
bottomSpacing = false,
|
||||
showAllBlocks = false,
|
||||
showToday = true,
|
||||
|
|
@ -79,6 +81,7 @@ export const GanttChartRoot: FC<GanttChartRootProps> = observer((props) => {
|
|||
enableReorder={enableReorder}
|
||||
enableAddBlock={enableAddBlock}
|
||||
enableSelection={enableSelection}
|
||||
enableDependency={enableDependency}
|
||||
bottomSpacing={bottomSpacing}
|
||||
showAllBlocks={showAllBlocks}
|
||||
quickAdd={quickAdd}
|
||||
|
|
|
|||
|
|
@ -98,14 +98,14 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||
target_date?: string;
|
||||
}[]
|
||||
) =>
|
||||
issues.updateIssueDates(workspaceSlug.toString(), projectId.toString(), updates).catch(() => {
|
||||
issues.updateIssueDates(workspaceSlug.toString(), updates, projectId.toString()).catch(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: t("toast.error"),
|
||||
message: "Error while updating work item dates, Please try again Later",
|
||||
});
|
||||
}),
|
||||
[issues]
|
||||
[issues, projectId, workspaceSlug]
|
||||
);
|
||||
|
||||
const quickAdd =
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||
}
|
||||
}, [isOpen, isMobile]);
|
||||
|
||||
if (!value) return null;
|
||||
|
||||
let projectLabels: IIssueLabel[] = defaultOptions as IIssueLabel[];
|
||||
if (storeLabels && storeLabels.length > 0) projectLabels = storeLabels;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,20 @@
|
|||
import React, { useCallback } from "react";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { isEmpty } from "lodash";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
// plane constants
|
||||
import {
|
||||
ALL_ISSUES,
|
||||
EIssueLayoutTypes,
|
||||
EIssueFilterType,
|
||||
EIssuesStoreType,
|
||||
ISSUE_DISPLAY_FILTERS_BY_PAGE
|
||||
,EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { IIssueDisplayFilterOptions } from "@plane/types";
|
||||
import useSWR from "swr";
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
||||
// hooks
|
||||
// components
|
||||
import { EmptyState } from "@/components/common";
|
||||
import { SpreadsheetView } from "@/components/issues/issue-layouts";
|
||||
import { AllIssueQuickActions } from "@/components/issues/issue-layouts/quick-action-dropdowns";
|
||||
import { SpreadsheetLayoutLoader } from "@/components/ui";
|
||||
// hooks
|
||||
import { useGlobalView, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { EmptyState } from "@/components/common";
|
||||
import { WorkspaceActiveLayout } from "@/components/views/helper";
|
||||
import { useGlobalView, useIssues } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { IssuesStoreContext } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||
// store
|
||||
import emptyView from "@/public/empty-state/view.svg";
|
||||
import { IssuePeekOverview } from "../../peek-overview";
|
||||
import { IssueLayoutHOC } from "../issue-layout-HOC";
|
||||
import { TRenderQuickActions } from "../list/list-view-types";
|
||||
|
||||
type Props = {
|
||||
isDefaultView: boolean;
|
||||
|
|
@ -38,32 +24,34 @@ type Props = {
|
|||
|
||||
export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
||||
const { isDefaultView, isLoading = false, toggleLoading } = props;
|
||||
// router
|
||||
const { workspaceSlug, globalViewId } = useParams();
|
||||
|
||||
// Router hooks
|
||||
const router = useAppRouter();
|
||||
const { workspaceSlug, globalViewId } = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
const routeFilters: {
|
||||
[key: string]: string;
|
||||
} = {};
|
||||
|
||||
// Store hooks
|
||||
const {
|
||||
issuesFilter: { filters, fetchFilters, updateFilters },
|
||||
issues: { clear, groupedIssueIds, fetchIssues, fetchNextIssues },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const { fetchAllGlobalViews, getViewDetailsById } = useGlobalView();
|
||||
|
||||
// Custom hooks
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
|
||||
// Derived values
|
||||
const viewDetails = getViewDetailsById(globalViewId?.toString());
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
const activeLayout: EIssueLayoutTypes | undefined = issueFilters?.displayFilters?.layout;
|
||||
|
||||
// Route filters
|
||||
const routeFilters: { [key: string]: string } = {};
|
||||
searchParams.forEach((value: string, key: string) => {
|
||||
routeFilters[key] = value;
|
||||
});
|
||||
//swr hook for fetching issue properties
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
// store
|
||||
const {
|
||||
issuesFilter: { filters, fetchFilters, updateFilters },
|
||||
issues: { clear, getIssueLoader, getPaginationData, groupedIssueIds, fetchIssues, fetchNextIssues },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const { updateIssue, removeIssue, archiveIssue } = useIssuesActions(EIssuesStoreType.GLOBAL);
|
||||
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { fetchAllGlobalViews, getViewDetailsById } = useGlobalView();
|
||||
|
||||
const viewDetails = getViewDetailsById(globalViewId?.toString());
|
||||
// filter init from the query params
|
||||
|
||||
// Apply route filters to store
|
||||
const routerFilterParams = () => {
|
||||
if (
|
||||
workspaceSlug &&
|
||||
|
|
@ -89,10 +77,12 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
}
|
||||
};
|
||||
|
||||
// Fetch next pages callback
|
||||
const fetchNextPages = useCallback(() => {
|
||||
if (workspaceSlug && globalViewId) fetchNextIssues(workspaceSlug.toString(), globalViewId.toString());
|
||||
}, [fetchNextIssues, workspaceSlug, globalViewId]);
|
||||
|
||||
// Fetch global views
|
||||
const { isLoading: globalViewsLoading } = useSWR(
|
||||
workspaceSlug ? `WORKSPACE_GLOBAL_VIEWS_${workspaceSlug}` : null,
|
||||
async () => {
|
||||
|
|
@ -103,6 +93,7 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
// Fetch issues
|
||||
const { isLoading: issuesLoading } = useSWR(
|
||||
workspaceSlug && globalViewId ? `WORKSPACE_GLOBAL_VIEW_ISSUES_${workspaceSlug}_${globalViewId}` : null,
|
||||
async () => {
|
||||
|
|
@ -126,54 +117,7 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
const canEditProperties = useCallback(
|
||||
(projectId: string | undefined) => {
|
||||
if (!projectId) return false;
|
||||
return allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
projectId
|
||||
);
|
||||
},
|
||||
[workspaceSlug]
|
||||
);
|
||||
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
|
||||
const handleDisplayFiltersUpdate = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
undefined,
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ ...updatedDisplayFilter },
|
||||
globalViewId.toString()
|
||||
);
|
||||
},
|
||||
[updateFilters, workspaceSlug, globalViewId]
|
||||
);
|
||||
|
||||
const renderQuickActions: TRenderQuickActions = useCallback(
|
||||
({ issue, parentRef, customActionButton, placement, portalElement }) => (
|
||||
<AllIssueQuickActions
|
||||
parentRef={parentRef}
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
portalElement={portalElement}
|
||||
readOnly={!canEditProperties(issue.project_id ?? undefined)}
|
||||
placements={placement}
|
||||
/>
|
||||
),
|
||||
[canEditProperties, removeIssue, updateIssue, archiveIssue]
|
||||
);
|
||||
|
||||
// when the call is not loading and the view does not exist and the view is not a default view, show empty state
|
||||
// Empty state
|
||||
if (!isLoading && !globalViewsLoading && !issuesLoading && !viewDetails && !isDefaultView) {
|
||||
return (
|
||||
<EmptyState
|
||||
|
|
@ -188,31 +132,18 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
);
|
||||
}
|
||||
|
||||
if ((isLoading && issuesLoading && getIssueLoader() === "init-loader") || !globalViewId || !groupedIssueIds) {
|
||||
return <SpreadsheetLayoutLoader />;
|
||||
}
|
||||
|
||||
const issueIds = groupedIssueIds[ALL_ISSUES];
|
||||
const nextPageResults = getPaginationData(ALL_ISSUES, undefined)?.nextPageResults;
|
||||
|
||||
return (
|
||||
<IssuesStoreContext.Provider value={EIssuesStoreType.GLOBAL}>
|
||||
<IssueLayoutHOC layout={EIssueLayoutTypes.SPREADSHEET}>
|
||||
<SpreadsheetView
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issueIds={Array.isArray(issueIds) ? issueIds : []}
|
||||
quickActions={renderQuickActions}
|
||||
updateIssue={updateIssue}
|
||||
canEditProperties={canEditProperties}
|
||||
canLoadMoreIssues={!!nextPageResults}
|
||||
loadMoreIssues={fetchNextPages}
|
||||
isWorkspaceLevel
|
||||
/>
|
||||
{/* peek overview */}
|
||||
<IssuePeekOverview />
|
||||
</IssueLayoutHOC>
|
||||
</IssuesStoreContext.Provider>
|
||||
<WorkspaceActiveLayout
|
||||
activeLayout={activeLayout}
|
||||
isDefaultView={isDefaultView}
|
||||
isLoading={isLoading}
|
||||
toggleLoading={toggleLoading}
|
||||
workspaceSlug={workspaceSlug?.toString()}
|
||||
globalViewId={globalViewId?.toString()}
|
||||
routeFilters={routeFilters}
|
||||
fetchNextPages={fetchNextPages}
|
||||
globalViewsLoading={globalViewsLoading}
|
||||
issuesLoading={issuesLoading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
import React, { useCallback } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane constants
|
||||
import {
|
||||
ALL_ISSUES,
|
||||
EIssueLayoutTypes,
|
||||
EIssueFilterType,
|
||||
EIssuesStoreType,
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
} from "@plane/constants";
|
||||
import { IIssueDisplayFilterOptions } from "@plane/types";
|
||||
// hooks
|
||||
// components
|
||||
import { SpreadsheetView } from "@/components/issues/issue-layouts";
|
||||
import { AllIssueQuickActions } from "@/components/issues/issue-layouts/quick-action-dropdowns";
|
||||
import { SpreadsheetLayoutLoader } from "@/components/ui";
|
||||
// hooks
|
||||
import { useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { IssuesStoreContext } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||
// store
|
||||
import { IssuePeekOverview } from "../../../peek-overview";
|
||||
import { IssueLayoutHOC } from "../../issue-layout-HOC";
|
||||
import { TRenderQuickActions } from "../../list/list-view-types";
|
||||
|
||||
type Props = {
|
||||
isDefaultView: boolean;
|
||||
isLoading?: boolean;
|
||||
toggleLoading: (value: boolean) => void;
|
||||
workspaceSlug: string;
|
||||
globalViewId: string;
|
||||
routeFilters: {
|
||||
[key: string]: string;
|
||||
};
|
||||
fetchNextPages: () => void;
|
||||
globalViewsLoading: boolean;
|
||||
issuesLoading: boolean;
|
||||
};
|
||||
|
||||
export const WorkspaceSpreadsheetRoot: React.FC<Props> = observer((props: Props) => {
|
||||
const { isLoading = false, workspaceSlug, globalViewId, fetchNextPages, issuesLoading } = props;
|
||||
|
||||
// Custom hooks
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
|
||||
// Store hooks
|
||||
const {
|
||||
issuesFilter: { filters, updateFilters },
|
||||
issues: { getIssueLoader, getPaginationData, groupedIssueIds },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const { updateIssue, removeIssue, archiveIssue } = useIssuesActions(EIssuesStoreType.GLOBAL);
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// Derived values
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
|
||||
// Permission checker
|
||||
const canEditProperties = useCallback(
|
||||
(projectId: string | undefined) => {
|
||||
if (!projectId) return false;
|
||||
return allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
projectId
|
||||
);
|
||||
},
|
||||
[allowPermissions, workspaceSlug]
|
||||
);
|
||||
|
||||
// Display filters handler
|
||||
const handleDisplayFiltersUpdate = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
undefined,
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ ...updatedDisplayFilter },
|
||||
globalViewId.toString()
|
||||
);
|
||||
},
|
||||
[updateFilters, workspaceSlug, globalViewId]
|
||||
);
|
||||
|
||||
// Quick actions renderer
|
||||
const renderQuickActions: TRenderQuickActions = useCallback(
|
||||
({ issue, parentRef, customActionButton, placement, portalElement }) => (
|
||||
<AllIssueQuickActions
|
||||
parentRef={parentRef}
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
portalElement={portalElement}
|
||||
readOnly={!canEditProperties(issue.project_id ?? undefined)}
|
||||
placements={placement}
|
||||
/>
|
||||
),
|
||||
[canEditProperties, removeIssue, updateIssue, archiveIssue]
|
||||
);
|
||||
|
||||
// Loading state
|
||||
if ((isLoading && issuesLoading && getIssueLoader() === "init-loader") || !globalViewId || !groupedIssueIds) {
|
||||
return <SpreadsheetLayoutLoader />;
|
||||
}
|
||||
|
||||
// Computed values
|
||||
const issueIds = groupedIssueIds[ALL_ISSUES];
|
||||
const nextPageResults = getPaginationData(ALL_ISSUES, undefined)?.nextPageResults;
|
||||
|
||||
// Render spreadsheet
|
||||
return (
|
||||
<IssuesStoreContext.Provider value={EIssuesStoreType.GLOBAL}>
|
||||
<IssueLayoutHOC layout={EIssueLayoutTypes.SPREADSHEET}>
|
||||
<SpreadsheetView
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issueIds={Array.isArray(issueIds) ? issueIds : []}
|
||||
quickActions={renderQuickActions}
|
||||
updateIssue={updateIssue}
|
||||
canEditProperties={canEditProperties}
|
||||
canLoadMoreIssues={!!nextPageResults}
|
||||
loadMoreIssues={fetchNextPages}
|
||||
isWorkspaceLevel
|
||||
/>
|
||||
{/* peek overview */}
|
||||
<IssuePeekOverview />
|
||||
</IssueLayoutHOC>
|
||||
</IssuesStoreContext.Provider>
|
||||
);
|
||||
});
|
||||
51
web/core/components/views/helper.tsx
Normal file
51
web/core/components/views/helper.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { EIssueLayoutTypes } from "@plane/constants";
|
||||
import { WorkspaceAdditionalLayouts } from "@/plane-web/components/views/helper";
|
||||
import { WorkspaceSpreadsheetRoot } from "../issues/issue-layouts/spreadsheet/roots/workspace-root";
|
||||
|
||||
export type TWorkspaceLayoutProps = {
|
||||
activeLayout: EIssueLayoutTypes | undefined;
|
||||
isDefaultView: boolean;
|
||||
isLoading?: boolean;
|
||||
toggleLoading: (value: boolean) => void;
|
||||
workspaceSlug: string;
|
||||
globalViewId: string;
|
||||
routeFilters: {
|
||||
[key: string]: string;
|
||||
};
|
||||
fetchNextPages: () => void;
|
||||
globalViewsLoading: boolean;
|
||||
issuesLoading: boolean;
|
||||
};
|
||||
|
||||
export const WorkspaceActiveLayout = (props: TWorkspaceLayoutProps) => {
|
||||
const {
|
||||
activeLayout = EIssueLayoutTypes.SPREADSHEET,
|
||||
isDefaultView,
|
||||
isLoading,
|
||||
toggleLoading,
|
||||
workspaceSlug,
|
||||
globalViewId,
|
||||
routeFilters,
|
||||
fetchNextPages,
|
||||
globalViewsLoading,
|
||||
issuesLoading,
|
||||
} = props;
|
||||
switch (activeLayout) {
|
||||
case EIssueLayoutTypes.SPREADSHEET:
|
||||
return (
|
||||
<WorkspaceSpreadsheetRoot
|
||||
isDefaultView={isDefaultView}
|
||||
isLoading={isLoading}
|
||||
toggleLoading={toggleLoading}
|
||||
workspaceSlug={workspaceSlug}
|
||||
globalViewId={globalViewId}
|
||||
routeFilters={routeFilters}
|
||||
fetchNextPages={fetchNextPages}
|
||||
globalViewsLoading={globalViewsLoading}
|
||||
issuesLoading={issuesLoading}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <WorkspaceAdditionalLayouts {...props} />;
|
||||
}
|
||||
};
|
||||
|
|
@ -9,6 +9,7 @@ import { IProjectEpics, IProjectEpicsFilter } from "@/plane-web/store/issue/epic
|
|||
// types
|
||||
import { ITeamIssues, ITeamIssuesFilter } from "@/plane-web/store/issue/team";
|
||||
import { ITeamViewIssues, ITeamViewIssuesFilter } from "@/plane-web/store/issue/team-views";
|
||||
import { IWorkspaceIssues } from "@/plane-web/store/issue/workspace/issue.store";
|
||||
import { IArchivedIssues, IArchivedIssuesFilter } from "@/store/issue/archived";
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "@/store/issue/cycle";
|
||||
import { IDraftIssues, IDraftIssuesFilter } from "@/store/issue/draft";
|
||||
|
|
@ -16,7 +17,7 @@ import { IModuleIssues, IModuleIssuesFilter } from "@/store/issue/module";
|
|||
import { IProfileIssues, IProfileIssuesFilter } from "@/store/issue/profile";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "@/store/issue/project";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "@/store/issue/project-views";
|
||||
import { IWorkspaceIssues, IWorkspaceIssuesFilter } from "@/store/issue/workspace";
|
||||
import { IWorkspaceIssuesFilter } from "@/store/issue/workspace";
|
||||
import { IWorkspaceDraftIssues, IWorkspaceDraftIssuesFilter } from "@/store/issue/workspace-draft";
|
||||
// constants
|
||||
|
||||
|
|
|
|||
|
|
@ -263,8 +263,11 @@ export class WorkspaceService extends APIService {
|
|||
}
|
||||
|
||||
async getViewIssues(workspaceSlug: string, params: any, config = {}): Promise<TIssuesResponse> {
|
||||
const path = params.expand?.includes("issue_relation")
|
||||
? `/api/workspaces/${workspaceSlug}/issues-detail/`
|
||||
: `/api/workspaces/${workspaceSlug}/issues/`;
|
||||
return this.get(
|
||||
`/api/workspaces/${workspaceSlug}/issues/`,
|
||||
path,
|
||||
{
|
||||
params,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export interface IBaseIssuesStore {
|
|||
addModuleIds: string[],
|
||||
removeModuleIds: string[]
|
||||
): Promise<void>;
|
||||
updateIssueDates(workspaceSlug: string, projectId: string, updates: IBlockUpdateDependencyData[]): Promise<void>;
|
||||
updateIssueDates(workspaceSlug: string, updates: IBlockUpdateDependencyData[], projectId?: string): Promise<void>;
|
||||
}
|
||||
|
||||
// This constant maps the group by keys to the respective issue property that the key relies on
|
||||
|
|
@ -826,9 +826,10 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
|||
|
||||
async updateIssueDates(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
updates: { id: string; start_date?: string; target_date?: string }[]
|
||||
updates: { id: string; start_date?: string; target_date?: string }[],
|
||||
projectId?: string
|
||||
) {
|
||||
if(!projectId) return;
|
||||
const issueDatesBeforeChange: { id: string; start_date?: string; target_date?: string }[] = [];
|
||||
try {
|
||||
const getIssueById = this.rootIssueStore.issues.getIssueById;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ export class IssueRelationStore implements IIssueRelationStore {
|
|||
*/
|
||||
createCurrentRelation = async (issueId: string, relationType: TIssueRelationTypes, relatedIssueId: string) => {
|
||||
const workspaceSlug = this.rootIssueDetailStore.rootIssueStore.workspaceSlug;
|
||||
const projectId = this.rootIssueDetailStore.rootIssueStore.projectId;
|
||||
const projectId = this.rootIssueDetailStore.issue.getIssueById(issueId)?.project_id;
|
||||
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
TeamViewIssuesFilter,
|
||||
} from "@/plane-web/store/issue/team-views";
|
||||
// root store
|
||||
import { IWorkspaceIssues, WorkspaceIssues } from "@/plane-web/store/issue/workspace/issue.store";
|
||||
import { RootStore } from "@/plane-web/store/root.store";
|
||||
import { IWorkspaceMembership } from "@/store/member/workspace-member.store";
|
||||
// issues data store
|
||||
|
|
@ -32,7 +33,7 @@ import {
|
|||
IProjectViewIssues,
|
||||
ProjectViewIssues,
|
||||
} from "./project-views";
|
||||
import { WorkspaceIssuesFilter, IWorkspaceIssues, WorkspaceIssues, IWorkspaceIssuesFilter } from "./workspace";
|
||||
import { WorkspaceIssuesFilter, IWorkspaceIssuesFilter } from "./workspace";
|
||||
import {
|
||||
IWorkspaceDraftIssues,
|
||||
IWorkspaceDraftIssuesFilter,
|
||||
|
|
|
|||
|
|
@ -132,53 +132,49 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
|||
);
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, viewId: TWorkspaceFilters) => {
|
||||
try {
|
||||
let filters: IIssueFilterOptions;
|
||||
let displayFilters: IIssueDisplayFilterOptions;
|
||||
let displayProperties: IIssueDisplayProperties;
|
||||
let kanbanFilters: TIssueKanbanFilters = {
|
||||
group_by: [],
|
||||
sub_group_by: [],
|
||||
};
|
||||
let filters: IIssueFilterOptions;
|
||||
let displayFilters: IIssueDisplayFilterOptions;
|
||||
let displayProperties: IIssueDisplayProperties;
|
||||
let kanbanFilters: TIssueKanbanFilters = {
|
||||
group_by: [],
|
||||
sub_group_by: [],
|
||||
};
|
||||
|
||||
const _filters = this.handleIssuesLocalFilters.get(EIssuesStoreType.GLOBAL, workspaceSlug, undefined, viewId);
|
||||
const _filters = this.handleIssuesLocalFilters.get(EIssuesStoreType.GLOBAL, workspaceSlug, undefined, viewId);
|
||||
displayFilters = this.computedDisplayFilters(_filters?.display_filters, {
|
||||
layout: EIssueLayoutTypes.SPREADSHEET,
|
||||
order_by: "-created_at",
|
||||
});
|
||||
displayProperties = this.computedDisplayProperties(_filters?.display_properties);
|
||||
kanbanFilters = {
|
||||
group_by: _filters?.kanban_filters?.group_by || [],
|
||||
sub_group_by: _filters?.kanban_filters?.sub_group_by || [],
|
||||
};
|
||||
|
||||
if (["all-issues", "assigned", "created", "subscribed"].includes(viewId)) {
|
||||
const currentUserId = this.rootIssueStore.currentUserId;
|
||||
filters = this.getComputedFiltersBasedOnViews(currentUserId, viewId as TStaticViewTypes);
|
||||
} else {
|
||||
const _filters = await this.issueFilterService.getViewDetails(workspaceSlug, viewId);
|
||||
filters = this.computedFilters(_filters?.filters);
|
||||
displayFilters = this.computedDisplayFilters(_filters?.display_filters, {
|
||||
layout: EIssueLayoutTypes.SPREADSHEET,
|
||||
order_by: "-created_at",
|
||||
});
|
||||
displayProperties = this.computedDisplayProperties(_filters?.display_properties);
|
||||
kanbanFilters = {
|
||||
group_by: _filters?.kanban_filters?.group_by || [],
|
||||
sub_group_by: _filters?.kanban_filters?.sub_group_by || [],
|
||||
};
|
||||
|
||||
if (["all-issues", "assigned", "created", "subscribed"].includes(viewId)) {
|
||||
const currentUserId = this.rootIssueStore.currentUserId;
|
||||
filters = this.getComputedFiltersBasedOnViews(currentUserId, viewId as TStaticViewTypes);
|
||||
} else {
|
||||
const _filters = await this.issueFilterService.getViewDetails(workspaceSlug, viewId);
|
||||
filters = this.computedFilters(_filters?.filters);
|
||||
displayFilters = this.computedDisplayFilters(_filters?.display_filters, {
|
||||
layout: EIssueLayoutTypes.SPREADSHEET,
|
||||
order_by: "-created_at",
|
||||
});
|
||||
displayProperties = this.computedDisplayProperties(_filters?.display_properties);
|
||||
}
|
||||
|
||||
// override existing order by if ordered by manual sort_order
|
||||
if (displayFilters.order_by === "sort_order") {
|
||||
displayFilters.order_by = "-created_at";
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
set(this.filters, [viewId, "filters"], filters);
|
||||
set(this.filters, [viewId, "displayFilters"], displayFilters);
|
||||
set(this.filters, [viewId, "displayProperties"], displayProperties);
|
||||
set(this.filters, [viewId, "kanbanFilters"], kanbanFilters);
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// override existing order by if ordered by manual sort_order
|
||||
if (displayFilters.order_by === "sort_order") {
|
||||
displayFilters.order_by = "-created_at";
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
set(this.filters, [viewId, "filters"], filters);
|
||||
set(this.filters, [viewId, "displayFilters"], displayFilters);
|
||||
set(this.filters, [viewId, "displayProperties"], displayProperties);
|
||||
set(this.filters, [viewId, "kanbanFilters"], kanbanFilters);
|
||||
});
|
||||
};
|
||||
|
||||
updateFilters = async (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue