forked from github/plane
fix: enable global/ all issues (#3405)
* fix global issues and views * remove separate layouts for specific views * add permissions to views * fix global issues filters --------- Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local>
This commit is contained in:
parent
c9337d4a41
commit
ea3a0362b0
@ -12,7 +12,12 @@ import { Breadcrumbs, Button, LayersIcon, PhotoFilterIcon, Tooltip } from "@plan
|
||||
// icons
|
||||
import { List, PlusIcon, Sheet } from "lucide-react";
|
||||
// types
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types";
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
IIssueFilterOptions,
|
||||
TStaticViewTypes,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
@ -35,7 +40,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
||||
const { workspaceSlug, globalViewId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
issuesFilter: { filters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
@ -47,6 +52,8 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
||||
workspace: { workspaceMemberIds },
|
||||
} = useMember();
|
||||
|
||||
const issueFilters = globalViewId ? filters[globalViewId.toString()] : undefined;
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
|
@ -25,13 +25,14 @@ type Props = {
|
||||
handleRemoveFilter: (key: keyof IIssueFilterOptions, value: string | null) => void;
|
||||
labels?: IIssueLabel[] | undefined;
|
||||
states?: IState[] | undefined;
|
||||
alwaysAllowEditing?: boolean;
|
||||
};
|
||||
|
||||
const membersFilters = ["assignees", "mentions", "created_by", "subscriber"];
|
||||
const dateFilters = ["start_date", "target_date"];
|
||||
|
||||
export const AppliedFiltersList: React.FC<Props> = observer((props) => {
|
||||
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, labels, states } = props;
|
||||
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, labels, states, alwaysAllowEditing } = props;
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
@ -41,7 +42,7 @@ export const AppliedFiltersList: React.FC<Props> = observer((props) => {
|
||||
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
const isEditingAllowed = currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100">
|
||||
|
@ -15,13 +15,13 @@ export const AppliedMembersFilters: React.FC<Props> = observer((props) => {
|
||||
const { handleRemove, values, editable } = props;
|
||||
|
||||
const {
|
||||
project: { getProjectMemberDetails },
|
||||
workspace: { getWorkspaceMemberDetails },
|
||||
} = useMember();
|
||||
|
||||
return (
|
||||
<>
|
||||
{values.map((memberId) => {
|
||||
const memberDetails = getProjectMemberDetails(memberId)?.member;
|
||||
const memberDetails = getWorkspaceMemberDetails(memberId)?.member;
|
||||
|
||||
if (!memberDetails) return null;
|
||||
|
||||
|
@ -1,32 +1,48 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import isEqual from "lodash/isEqual";
|
||||
// hooks
|
||||
import { useIssues, useLabel } from "hooks/store";
|
||||
import { useGlobalView, useIssues, useLabel, useUser } from "hooks/store";
|
||||
//ui
|
||||
import { Button } from "@plane/ui";
|
||||
// components
|
||||
import { AppliedFiltersList } from "components/issues";
|
||||
// types
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
import { IIssueFilterOptions, TStaticViewTypes } from "@plane/types";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "constants/workspace";
|
||||
|
||||
export const GlobalViewsAppliedFiltersRoot = observer(() => {
|
||||
type Props = {
|
||||
globalViewId: string;
|
||||
};
|
||||
|
||||
export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
||||
const { globalViewId } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, globalViewId } = router.query;
|
||||
const { workspaceSlug } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
issuesFilter: { filters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const {
|
||||
workspace: { workspaceLabels },
|
||||
} = useLabel();
|
||||
const { globalViewMap, updateGlobalView } = useGlobalView();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
|
||||
// derived values
|
||||
const userFilters = issueFilters?.filters;
|
||||
const userFilters = filters?.[globalViewId]?.filters;
|
||||
const viewDetails = globalViewMap[globalViewId];
|
||||
|
||||
// filters whose value not null or empty array
|
||||
const appliedFilters: IIssueFilterOptions = {};
|
||||
let appliedFilters: IIssueFilterOptions | undefined = undefined;
|
||||
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
||||
if (!value) return;
|
||||
if (Array.isArray(value) && value.length === 0) return;
|
||||
if (!appliedFilters) appliedFilters = {};
|
||||
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
||||
});
|
||||
|
||||
@ -70,29 +86,24 @@ export const GlobalViewsAppliedFiltersRoot = observer(() => {
|
||||
);
|
||||
};
|
||||
|
||||
// const handleUpdateView = () => {
|
||||
// if (!workspaceSlug || !globalViewId || !viewDetails) return;
|
||||
const handleUpdateView = () => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
|
||||
// globalViewsStore.updateGlobalView(workspaceSlug.toString(), globalViewId.toString(), {
|
||||
// query_data: {
|
||||
// ...viewDetails.query_data,
|
||||
// filters: {
|
||||
// ...(storedFilters ?? {}),
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
updateGlobalView(workspaceSlug.toString(), globalViewId.toString(), {
|
||||
filters: {
|
||||
...(appliedFilters ?? {}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// update stored filters when view details are fetched
|
||||
// useEffect(() => {
|
||||
// if (!globalViewId || !viewDetails) return;
|
||||
const areFiltersEqual = isEqual(appliedFilters, viewDetails?.filters);
|
||||
|
||||
// if (!globalViewFiltersStore.storedFilters[globalViewId.toString()])
|
||||
// globalViewFiltersStore.updateStoredFilters(globalViewId.toString(), viewDetails?.query_data?.filters ?? {});
|
||||
// }, [globalViewId, globalViewFiltersStore, viewDetails]);
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
const isDefaultView = DEFAULT_GLOBAL_VIEWS_LIST.map((view) => view.key).includes(globalViewId as TStaticViewTypes);
|
||||
|
||||
// return if no filters are applied
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
if (!appliedFilters && areFiltersEqual) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-start justify-between gap-4 p-4">
|
||||
@ -101,13 +112,17 @@ export const GlobalViewsAppliedFiltersRoot = observer(() => {
|
||||
appliedFilters={appliedFilters ?? {}}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
alwaysAllowEditing
|
||||
/>
|
||||
|
||||
{/* {storedFilters && viewDetails && areFiltersDifferent(storedFilters, viewDetails.query_data.filters ?? {}) && (
|
||||
<Button variant="primary" onClick={handleUpdateView}>
|
||||
Update view
|
||||
</Button>
|
||||
)} */}
|
||||
{!isDefaultView && !areFiltersEqual && isAuthorizedUser && (
|
||||
<>
|
||||
<div />
|
||||
<Button variant="primary" onClick={handleUpdateView}>
|
||||
Update view
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import isEqual from "lodash/isEqual";
|
||||
// hooks
|
||||
import { useIssues, useLabel, useProjectState, useProjectView } from "hooks/store";
|
||||
// components
|
||||
import { AppliedFiltersList } from "components/issues";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// helpers
|
||||
import { areFiltersDifferent } from "helpers/filter.helper";
|
||||
// types
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
@ -33,10 +32,11 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
const viewDetails = viewId ? viewMap[viewId.toString()] : null;
|
||||
const userFilters = issueFilters?.filters;
|
||||
// filters whose value not null or empty array
|
||||
const appliedFilters: IIssueFilterOptions = {};
|
||||
let appliedFilters: IIssueFilterOptions | undefined = undefined;
|
||||
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
||||
if (!value) return;
|
||||
if (Array.isArray(value) && value.length === 0) return;
|
||||
if (!appliedFilters) appliedFilters = {};
|
||||
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
||||
});
|
||||
|
||||
@ -78,9 +78,9 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { ...newFilters }, viewId);
|
||||
};
|
||||
|
||||
const areFiltersEqual = isEqual(appliedFilters, viewDetails?.filters);
|
||||
// return if no filters are applied
|
||||
if (Object.keys(appliedFilters).length === 0 && !areFiltersDifferent(appliedFilters, viewDetails?.filters ?? {}))
|
||||
return null;
|
||||
if (!appliedFilters && areFiltersEqual) return null;
|
||||
|
||||
const handleUpdateView = () => {
|
||||
if (!workspaceSlug || !projectId || !viewId || !viewDetails) return;
|
||||
@ -95,19 +95,23 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 p-4">
|
||||
<AppliedFiltersList
|
||||
appliedFilters={appliedFilters}
|
||||
appliedFilters={appliedFilters ?? {}}
|
||||
handleClearAllFilters={handleClearAllFilters}
|
||||
handleRemoveFilter={handleRemoveFilter}
|
||||
labels={projectLabels ?? []}
|
||||
states={projectStates}
|
||||
alwaysAllowEditing
|
||||
/>
|
||||
|
||||
{viewDetails?.filters && areFiltersDifferent(appliedFilters, viewDetails?.filters ?? {}) && (
|
||||
<div className="flex flex-shrink-0 items-center justify-center">
|
||||
<Button variant="primary" size="sm" onClick={handleUpdateView}>
|
||||
Update view
|
||||
</Button>
|
||||
</div>
|
||||
{!areFiltersEqual && (
|
||||
<>
|
||||
<div />
|
||||
<div className="flex flex-shrink-0 items-center justify-center">
|
||||
<Button variant="primary" size="sm" onClick={handleUpdateView}>
|
||||
Update view
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -16,43 +16,43 @@ import { EIssueActions } from "../types";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
|
||||
type Props = {
|
||||
type?: TStaticViewTypes | null;
|
||||
};
|
||||
|
||||
export const AllIssueLayoutRoot: React.FC<Props> = observer((props) => {
|
||||
const { type = null } = props;
|
||||
|
||||
export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, globalViewId } = router.query as { workspaceSlug: string; globalViewId: string };
|
||||
const { workspaceSlug, globalViewId } = router.query;
|
||||
|
||||
// store
|
||||
const {
|
||||
issuesFilter: { issueFilters, fetchFilters, updateFilters },
|
||||
issuesFilter: { filters, fetchFilters, updateFilters },
|
||||
issues: { loader, groupedIssueIds, fetchIssues, updateIssue, removeIssue },
|
||||
issueMap,
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
|
||||
const { dataViewId, issueIds } = groupedIssueIds;
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { fetchAllGlobalViews } = useGlobalView();
|
||||
// derived values
|
||||
const currentIssueView = type ?? globalViewId;
|
||||
|
||||
useSWR(workspaceSlug ? `WORKSPACE_GLOBAL_VIEWS${workspaceSlug}` : null, async () => {
|
||||
if (workspaceSlug) {
|
||||
await fetchAllGlobalViews(workspaceSlug);
|
||||
await fetchAllGlobalViews(workspaceSlug.toString());
|
||||
}
|
||||
});
|
||||
|
||||
useSWR(
|
||||
workspaceSlug && currentIssueView ? `WORKSPACE_GLOBAL_VIEW_ISSUES_${workspaceSlug}_${currentIssueView}` : null,
|
||||
workspaceSlug && globalViewId ? `WORKSPACE_GLOBAL_VIEW_ISSUES_${workspaceSlug}_${globalViewId}` : null,
|
||||
async () => {
|
||||
if (workspaceSlug && currentIssueView) {
|
||||
await fetchAllGlobalViews(workspaceSlug);
|
||||
await fetchFilters(workspaceSlug, currentIssueView);
|
||||
await fetchIssues(workspaceSlug, currentIssueView, groupedIssueIds ? "mutation" : "init-loader");
|
||||
if (workspaceSlug && globalViewId) {
|
||||
await fetchAllGlobalViews(workspaceSlug.toString());
|
||||
await fetchFilters(workspaceSlug.toString(), globalViewId.toString());
|
||||
await fetchIssues(
|
||||
workspaceSlug.toString(),
|
||||
globalViewId.toString(),
|
||||
groupedIssueIds ? "mutation" : "init-loader"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -65,22 +65,21 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props) => {
|
||||
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
};
|
||||
|
||||
const issueIds = (groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
const issuesArray = issueIds?.filter((id: string) => id && issueMap?.[id]).map((id: string) => issueMap?.[id]);
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
const projectId = issue.project_id;
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !globalViewId) return;
|
||||
|
||||
await updateIssue(workspaceSlug, projectId, issue.id, issue, currentIssueView);
|
||||
await updateIssue(workspaceSlug.toString(), projectId, issue.id, issue, globalViewId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
const projectId = issue.project_id;
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !globalViewId) return;
|
||||
|
||||
await removeIssue(workspaceSlug, projectId, issue.id, currentIssueView);
|
||||
await removeIssue(workspaceSlug.toString(), projectId, issue.id, globalViewId.toString());
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -100,22 +99,22 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props) => {
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
updateFilters(workspaceSlug, undefined, EIssueFilterType.DISPLAY_FILTERS, { ...updatedDisplayFilter });
|
||||
updateFilters(workspaceSlug.toString(), undefined, EIssueFilterType.DISPLAY_FILTERS, { ...updatedDisplayFilter });
|
||||
},
|
||||
[updateFilters, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||
{globalViewId != currentIssueView && (loader === "init-loader" || !groupedIssueIds) ? (
|
||||
{!globalViewId || globalViewId !== dataViewId || loader === "init-loader" || !issueIds ? (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<GlobalViewsAppliedFiltersRoot />
|
||||
<GlobalViewsAppliedFiltersRoot globalViewId={globalViewId} />
|
||||
|
||||
{(groupedIssueIds ?? {}).length == 0 ? (
|
||||
{(issueIds ?? {}).length == 0 ? (
|
||||
<>{/* <GlobalViewEmptyState /> */}</>
|
||||
) : (
|
||||
<div className="relative h-full w-full overflow-auto">
|
||||
@ -123,7 +122,7 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props) => {
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issues={issuesArray}
|
||||
issueIds={issueIds}
|
||||
quickActions={(issue) => (
|
||||
<AllIssueQuickActions
|
||||
issue={issue}
|
||||
@ -133,7 +132,7 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props) => {
|
||||
)}
|
||||
handleIssues={handleIssues}
|
||||
canEditProperties={canEditProperties}
|
||||
viewId={currentIssueView}
|
||||
viewId={globalViewId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -30,7 +30,7 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
useSWR(
|
||||
workspaceSlug && projectId && viewId ? `PROJECT_VIEW_ISSUES_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId && viewId ? `PROJECT_VIEW_ISSUES_${workspaceSlug}_${projectId}_${viewId}` : null,
|
||||
async () => {
|
||||
if (workspaceSlug && projectId && viewId) {
|
||||
await issuesFilter?.fetchFilters(workspaceSlug.toString(), projectId.toString(), viewId.toString());
|
||||
|
@ -58,8 +58,6 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||
|
||||
const issueIds = (issueStore.groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
|
||||
const issues = issueIds?.filter((id) => id && issueMap?.[id]).map((id) => issueMap?.[id]);
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
@ -109,7 +107,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||
displayProperties={issueFiltersStore.issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issueFiltersStore.issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issues={issues}
|
||||
issueIds={issueIds}
|
||||
quickActions={renderQuickActions}
|
||||
handleIssues={handleIssues}
|
||||
canEditProperties={canEditProperties}
|
||||
|
@ -10,7 +10,7 @@ type Props = {
|
||||
displayProperties: IIssueDisplayProperties;
|
||||
displayFilters: IIssueDisplayFilterOptions;
|
||||
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
||||
issues: TIssue[];
|
||||
issueIds: string[];
|
||||
isEstimateEnabled: boolean;
|
||||
quickActions: (
|
||||
issue: TIssue,
|
||||
@ -27,7 +27,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
||||
displayProperties,
|
||||
displayFilters,
|
||||
handleDisplayFilterUpdate,
|
||||
issues,
|
||||
issueIds,
|
||||
isEstimateEnabled,
|
||||
portalElement,
|
||||
quickActions,
|
||||
@ -44,7 +44,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
/>
|
||||
<tbody>
|
||||
{issues.map(({ id }) => (
|
||||
{issueIds.map((id) => (
|
||||
<SpreadsheetIssueRow
|
||||
key={id}
|
||||
issueId={id}
|
||||
|
@ -14,7 +14,7 @@ type Props = {
|
||||
displayProperties: IIssueDisplayProperties;
|
||||
displayFilters: IIssueDisplayFilterOptions;
|
||||
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
||||
issues: TIssue[] | undefined;
|
||||
issueIds: string[] | undefined;
|
||||
quickActions: (
|
||||
issue: TIssue,
|
||||
customActionButton?: React.ReactElement,
|
||||
@ -39,7 +39,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
displayProperties,
|
||||
displayFilters,
|
||||
handleDisplayFilterUpdate,
|
||||
issues,
|
||||
issueIds,
|
||||
quickActions,
|
||||
handleIssues,
|
||||
quickAddCallback,
|
||||
@ -91,7 +91,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!issues || issues.length === 0)
|
||||
if (!issueIds || issueIds.length === 0)
|
||||
return (
|
||||
<div className="grid h-full w-full place-items-center">
|
||||
<Spinner />
|
||||
@ -106,7 +106,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
displayProperties={displayProperties}
|
||||
displayFilters={displayFilters}
|
||||
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
||||
issues={issues}
|
||||
issueIds={issueIds}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
portalElement={portalRef}
|
||||
quickActions={quickActions}
|
||||
|
@ -61,12 +61,12 @@ export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
||||
});
|
||||
}, [data, preLoadedData, reset]);
|
||||
|
||||
const selectedFilters = watch("query_data")?.filters;
|
||||
const selectedFilters = watch("filters");
|
||||
|
||||
const clearAllFilters = () => {
|
||||
if (!selectedFilters) return;
|
||||
|
||||
setValue("query_data.filters", {});
|
||||
setValue("filters", {});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -120,7 +120,7 @@ export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
||||
<div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="query_data.filters"
|
||||
name="filters"
|
||||
render={({ field: { onChange, value: filters } }) => (
|
||||
<FiltersDropdown title="Filters">
|
||||
<FilterSelection
|
||||
|
@ -4,11 +4,11 @@ import Link from "next/link";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Plus } from "lucide-react";
|
||||
// store hooks
|
||||
import { useGlobalView } from "hooks/store";
|
||||
import { useGlobalView, useUser } from "hooks/store";
|
||||
// components
|
||||
import { CreateUpdateWorkspaceViewModal } from "components/workspace";
|
||||
// constants
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "constants/workspace";
|
||||
|
||||
const ViewTab = observer((props: { viewId: string }) => {
|
||||
const { viewId } = props;
|
||||
@ -46,6 +46,9 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
||||
const { workspaceSlug, globalViewId } = router.query;
|
||||
// store hooks
|
||||
const { currentWorkspaceViews } = useGlobalView();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
|
||||
// bring the active view to the centre of the header
|
||||
useEffect(() => {
|
||||
@ -56,7 +59,8 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
||||
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
|
||||
}, [globalViewId]);
|
||||
|
||||
const isTabSelected = (tabKey: string) => router.pathname.includes(tabKey);
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
||||
@ -66,7 +70,7 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
||||
<Link key={tab.key} href={`/${workspaceSlug}/workspace-views/${tab.key}`}>
|
||||
<span
|
||||
className={`flex min-w-min flex-shrink-0 whitespace-nowrap border-b-2 p-3 text-sm font-medium outline-none ${
|
||||
isTabSelected(tab.key)
|
||||
tab.key === globalViewId
|
||||
? "border-custom-primary-100 text-custom-primary-100"
|
||||
: "border-transparent hover:border-custom-border-200 hover:text-custom-text-400"
|
||||
}`}
|
||||
@ -81,13 +85,15 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="sticky -right-4 flex w-12 flex-shrink-0 items-center justify-center border-transparent bg-custom-background-100 py-3 hover:border-custom-border-200 hover:text-custom-text-400"
|
||||
onClick={() => setCreateViewModal(true)}
|
||||
>
|
||||
<Plus className="h-4 w-4 text-custom-primary-200" />
|
||||
</button>
|
||||
{isAuthorizedUser && (
|
||||
<button
|
||||
type="button"
|
||||
className="sticky -right-4 flex w-12 flex-shrink-0 items-center justify-center border-transparent bg-custom-background-100 py-3 hover:border-custom-border-200 hover:text-custom-text-400"
|
||||
onClick={() => setCreateViewModal(true)}
|
||||
>
|
||||
<Plus className="h-4 w-4 text-custom-primary-200" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -36,8 +36,8 @@ export const CreateUpdateWorkspaceViewModal: React.FC<Props> = observer((props)
|
||||
|
||||
const payloadData: Partial<IWorkspaceView> = {
|
||||
...payload,
|
||||
query: {
|
||||
...payload.query_data?.filters,
|
||||
filters: {
|
||||
...payload?.filters,
|
||||
},
|
||||
};
|
||||
|
||||
@ -67,7 +67,7 @@ export const CreateUpdateWorkspaceViewModal: React.FC<Props> = observer((props)
|
||||
const payloadData: Partial<IWorkspaceView> = {
|
||||
...payload,
|
||||
query: {
|
||||
...payload.query_data?.filters,
|
||||
...payload?.filters,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const GlobalViewListItem: React.FC<Props> = observer((props) => {
|
||||
|
||||
if (!view) return null;
|
||||
|
||||
const totalFilters = calculateTotalFilters(view.query_data.filters ?? {});
|
||||
const totalFilters = calculateTotalFilters(view.filters ?? {});
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -14,19 +14,3 @@ export const calculateTotalFilters = (filters: IIssueFilterOptions): number =>
|
||||
.reduce((curr, prev) => curr + prev, 0)
|
||||
: 0;
|
||||
|
||||
// check if there is any difference between the saved filters and the current filters
|
||||
export const areFiltersDifferent = (filtersSet1: IIssueFilterOptions, filtersSet2: IIssueFilterOptions) => {
|
||||
for (const [key, value] of Object.entries(filtersSet1) as [keyof IIssueFilterOptions, string[] | null][]) {
|
||||
if (value) {
|
||||
if (Array.isArray(value) && Array.isArray(filtersSet2[key])) {
|
||||
if (value.length !== filtersSet2[key]?.length) return true;
|
||||
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (!filtersSet2[key]?.includes(value[i])) return true;
|
||||
}
|
||||
} else if (value !== filtersSet2[key]) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { ReactElement } from "react";
|
||||
// components
|
||||
import { GlobalViewsHeader } from "components/workspace";
|
||||
import { GlobalIssuesHeader } from "components/headers";
|
||||
import { AllIssueLayoutRoot } from "components/issues/issue-layouts";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
|
||||
const GlobalViewAllIssuesPage: NextPageWithLayout = () => (
|
||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
||||
<GlobalViewsHeader />
|
||||
<AllIssueLayoutRoot type="all-issues" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
GlobalViewAllIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
||||
};
|
||||
|
||||
export default GlobalViewAllIssuesPage;
|
@ -1,24 +0,0 @@
|
||||
import { ReactElement } from "react";
|
||||
// components
|
||||
import { GlobalViewsHeader } from "components/workspace";
|
||||
import { GlobalIssuesHeader } from "components/headers";
|
||||
import { AllIssueLayoutRoot } from "components/issues/issue-layouts";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
|
||||
const GlobalViewAssignedIssuesPage: NextPageWithLayout = () => (
|
||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
||||
<GlobalViewsHeader />
|
||||
<AllIssueLayoutRoot type="assigned" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
GlobalViewAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
||||
};
|
||||
|
||||
export default GlobalViewAssignedIssuesPage;
|
@ -1,24 +0,0 @@
|
||||
import { ReactElement } from "react";
|
||||
// components
|
||||
import { GlobalViewsHeader } from "components/workspace";
|
||||
import { GlobalIssuesHeader } from "components/headers";
|
||||
import { AllIssueLayoutRoot } from "components/issues";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
|
||||
const GlobalViewCreatedIssuesPage: NextPageWithLayout = () => (
|
||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
||||
<GlobalViewsHeader />
|
||||
<AllIssueLayoutRoot type="created" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
GlobalViewCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
||||
};
|
||||
|
||||
export default GlobalViewCreatedIssuesPage;
|
@ -1,24 +0,0 @@
|
||||
import { ReactElement } from "react";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
// components
|
||||
import { GlobalViewsHeader } from "components/workspace";
|
||||
import { GlobalIssuesHeader } from "components/headers";
|
||||
import { AllIssueLayoutRoot } from "components/issues";
|
||||
// types
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
|
||||
const GlobalViewSubscribedIssuesPage: NextPageWithLayout = () => (
|
||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
||||
<GlobalViewsHeader />
|
||||
<AllIssueLayoutRoot type="subscribed" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
GlobalViewSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
||||
};
|
||||
|
||||
export default GlobalViewSubscribedIssuesPage;
|
@ -8,6 +8,7 @@ import {
|
||||
IIssueFiltersResponse,
|
||||
TIssueKanbanFilters,
|
||||
TIssueParams,
|
||||
TStaticViewTypes,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
import { isNil } from "constants/common";
|
||||
@ -109,6 +110,39 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
|
||||
subscriber: filters?.subscriber || null,
|
||||
});
|
||||
|
||||
/**
|
||||
* This PR is to get the filters of the fixed global views
|
||||
* @param currentUserId current logged in user id
|
||||
* @param type fixed view type
|
||||
* @returns filterOptions based on views
|
||||
*/
|
||||
getComputedFiltersBasedOnViews = (currentUserId: string | undefined, type: TStaticViewTypes) => {
|
||||
const noFilters = this.computedFilters({});
|
||||
|
||||
if (!currentUserId) return noFilters;
|
||||
|
||||
switch (type) {
|
||||
case "assigned":
|
||||
return {
|
||||
...noFilters,
|
||||
assignees: [currentUserId],
|
||||
};
|
||||
case "created":
|
||||
return {
|
||||
...noFilters,
|
||||
created_by: [currentUserId],
|
||||
};
|
||||
case "subscribed":
|
||||
return {
|
||||
...noFilters,
|
||||
subscriber: [currentUserId],
|
||||
};
|
||||
case "all-issues":
|
||||
default:
|
||||
return noFilters;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to apply the display filters on the issues
|
||||
* @param {IIssueDisplayFilterOptions} displayFilters
|
||||
|
@ -160,9 +160,6 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
|
||||
});
|
||||
|
||||
this.rootIssueStore.projectViewIssues.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
await this.issueFilterService.patchView(workspaceSlug, projectId, viewId, {
|
||||
filters: _filters.filters,
|
||||
});
|
||||
break;
|
||||
case EIssueFilterType.DISPLAY_FILTERS:
|
||||
const updatedDisplayFilters = filters as IIssueDisplayFilterOptions;
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
IIssueFilters,
|
||||
TIssueParams,
|
||||
TStaticViewTypes,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
@ -27,7 +28,7 @@ export interface IWorkspaceIssuesFilter {
|
||||
// computed
|
||||
issueFilters: IIssueFilters | undefined;
|
||||
appliedFilters: Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
// action
|
||||
// fetch action
|
||||
fetchFilters: (workspaceSlug: string, viewId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
workspaceSlug: string,
|
||||
@ -36,6 +37,9 @@ export interface IWorkspaceIssuesFilter {
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
//helper action
|
||||
getIssueFilters: (viewId: string | undefined) => IIssueFilters | undefined;
|
||||
getAppliedFilters: (viewId: string) => Partial<Record<TIssueParams, string | boolean>> | undefined;
|
||||
}
|
||||
|
||||
export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWorkspaceIssuesFilter {
|
||||
@ -54,9 +58,12 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
// computed
|
||||
issueFilters: computed,
|
||||
appliedFilters: computed,
|
||||
// actions
|
||||
// fetch actions
|
||||
fetchFilters: action,
|
||||
updateFilters: action,
|
||||
// helper actions
|
||||
getIssueFilters: action,
|
||||
getAppliedFilters: action,
|
||||
});
|
||||
// root store
|
||||
this.rootIssueStore = _rootStore;
|
||||
@ -64,8 +71,7 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
this.issueFilterService = new WorkspaceService();
|
||||
}
|
||||
|
||||
get issueFilters() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
getIssueFilters = (viewId: string | undefined) => {
|
||||
if (!viewId) return undefined;
|
||||
|
||||
const displayFilters = this.filters[viewId] || undefined;
|
||||
@ -74,10 +80,12 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
const _filters: IIssueFilters = this.computedIssueFilters(displayFilters);
|
||||
|
||||
return _filters;
|
||||
}
|
||||
};
|
||||
|
||||
get appliedFilters() {
|
||||
const userFilters = this.issueFilters;
|
||||
getAppliedFilters = (viewId: string | undefined) => {
|
||||
if (!viewId) return undefined;
|
||||
|
||||
const userFilters = this.getIssueFilters(viewId);
|
||||
if (!userFilters) return undefined;
|
||||
|
||||
const filteredParams = handleIssueQueryParamsByLayout(userFilters?.displayFilters?.layout, "issues");
|
||||
@ -92,6 +100,16 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
if (userFilters?.displayFilters?.layout === "gantt_chart") filteredRouteParams.start_target_date = true;
|
||||
|
||||
return filteredRouteParams;
|
||||
};
|
||||
|
||||
get issueFilters() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
return this.getIssueFilters(viewId);
|
||||
}
|
||||
|
||||
get appliedFilters() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
return this.getAppliedFilters(viewId);
|
||||
}
|
||||
|
||||
fetchFilters = async (workspaceSlug: string, viewId: TWorkspaceFilters) => {
|
||||
@ -105,15 +123,17 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
};
|
||||
|
||||
const _filters = this.handleIssuesLocalFilters.get(EIssuesStoreType.GLOBAL, workspaceSlug, undefined, viewId);
|
||||
filters = this.computedFilters(_filters?.filters);
|
||||
displayFilters = this.computedDisplayFilters(_filters?.displayFilters);
|
||||
displayProperties = this.computedDisplayProperties(_filters?.displayProperties);
|
||||
displayFilters = this.computedDisplayFilters(_filters?.display_filters);
|
||||
displayProperties = this.computedDisplayProperties(_filters?.display_properties);
|
||||
kanbanFilters = {
|
||||
group_by: _filters?.kanbanFilters?.group_by || [],
|
||||
sub_group_by: _filters?.kanbanFilters?.sub_group_by || [],
|
||||
group_by: _filters?.kanban_filters?.group_by || [],
|
||||
sub_group_by: _filters?.kanban_filters?.sub_group_by || [],
|
||||
};
|
||||
|
||||
if (!["all-issues", "assigned", "created", "subscribed"].includes(viewId)) {
|
||||
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);
|
||||
@ -160,16 +180,7 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
set(this.filters, [viewId, "filters", _key], updatedFilters[_key as keyof IIssueFilterOptions]);
|
||||
});
|
||||
});
|
||||
|
||||
this.rootIssueStore.workspaceIssues.fetchIssues(workspaceSlug, viewId, "mutation");
|
||||
if (["all-issues", "assigned", "created", "subscribed"].includes(viewId))
|
||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.GLOBAL, type, workspaceSlug, undefined, viewId, {
|
||||
filters: _filters.filters,
|
||||
});
|
||||
else
|
||||
await this.issueFilterService.updateView(workspaceSlug, viewId, {
|
||||
filters: _filters.filters,
|
||||
});
|
||||
break;
|
||||
case EIssueFilterType.DISPLAY_FILTERS:
|
||||
const updatedDisplayFilters = filters as IIssueDisplayFilterOptions;
|
||||
|
@ -15,7 +15,7 @@ export interface IWorkspaceIssues {
|
||||
issues: { [viewId: string]: string[] };
|
||||
viewFlags: ViewFlags;
|
||||
// computed
|
||||
groupedIssueIds: TUnGroupedIssues | undefined;
|
||||
groupedIssueIds: { dataViewId: string; issueIds: TUnGroupedIssues | undefined };
|
||||
// actions
|
||||
fetchIssues: (workspaceSlug: string, viewId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
createIssue: (
|
||||
@ -59,7 +59,7 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
makeObservable(this, {
|
||||
// observable
|
||||
loader: observable.ref,
|
||||
issues: observable.ref,
|
||||
issues: observable,
|
||||
// computed
|
||||
groupedIssueIds: computed,
|
||||
// action
|
||||
@ -77,30 +77,32 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
|
||||
get groupedIssueIds() {
|
||||
const viewId = this.rootIssueStore.globalViewId;
|
||||
if (!viewId) return undefined;
|
||||
if (!viewId) return { dataViewId: "", issueIds: undefined };
|
||||
|
||||
const displayFilters = this.rootIssueStore?.workspaceIssuesFilter?.issueFilters?.displayFilters;
|
||||
if (!displayFilters) return undefined;
|
||||
const displayFilters = this.rootIssueStore?.workspaceIssuesFilter?.filters?.[viewId]?.displayFilters;
|
||||
if (!displayFilters) return { dataViewId: viewId, issueIds: undefined };
|
||||
|
||||
const orderBy = displayFilters?.order_by;
|
||||
|
||||
const viewIssueIds = this.issues[viewId] ?? [];
|
||||
const viewIssueIds = this.issues[viewId];
|
||||
|
||||
if (!viewIssueIds) return { dataViewId: viewId, issueIds: undefined };
|
||||
|
||||
const _issues = this.rootStore.issues.getIssuesByIds(viewIssueIds);
|
||||
if (!_issues) return undefined;
|
||||
if (!_issues) return { dataViewId: viewId, issueIds: [] };
|
||||
|
||||
let issues: TIssue | TUnGroupedIssues | undefined = undefined;
|
||||
let issueIds: TIssue | TUnGroupedIssues | undefined = undefined;
|
||||
|
||||
issues = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
issueIds = this.unGroupedIssues(orderBy ?? "-created_at", _issues);
|
||||
|
||||
return issues;
|
||||
return { dataViewId: viewId, issueIds };
|
||||
}
|
||||
|
||||
fetchIssues = async (workspaceSlug: string, viewId: string, loadType: TLoader = "init-loader") => {
|
||||
try {
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.workspaceIssuesFilter?.appliedFilters;
|
||||
const params = this.rootIssueStore?.workspaceIssuesFilter?.getAppliedFilters(viewId);
|
||||
const response = await this.workspaceService.getViewIssues(workspaceSlug, params);
|
||||
|
||||
runInAction(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user