mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-994] fix: pages list mutation between projects (#4179)
* fix: pages list mutation between projects * fix: page tab logic * chore: remove pageType from the project pages store * chore: rename computed helper functions
This commit is contained in:
parent
d58bc03db6
commit
7507cb0a0f
@ -1,19 +1,24 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
// types
|
||||||
|
import { TPageNavigationTabs } from "@plane/types";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectPages } from "@/hooks/store";
|
import { useProjectPages } from "@/hooks/store";
|
||||||
// components
|
// components
|
||||||
import { PageListBlock } from "./";
|
import { PageListBlock } from "./";
|
||||||
|
|
||||||
type TPagesListRoot = {
|
type TPagesListRoot = {
|
||||||
workspaceSlug: string;
|
pageType: TPageNavigationTabs;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
workspaceSlug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PagesListRoot: FC<TPagesListRoot> = observer((props) => {
|
export const PagesListRoot: FC<TPagesListRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId } = props;
|
const { pageType, projectId, workspaceSlug } = props;
|
||||||
// hooks
|
// store hooks
|
||||||
const { filteredPageIds } = useProjectPages(projectId);
|
const { getCurrentProjectFilteredPageIds } = useProjectPages(projectId);
|
||||||
|
// derived values
|
||||||
|
const filteredPageIds = getCurrentProjectFilteredPageIds(pageType);
|
||||||
|
|
||||||
if (!filteredPageIds) return <></>;
|
if (!filteredPageIds) return <></>;
|
||||||
return (
|
return (
|
||||||
|
@ -42,21 +42,18 @@ export const PageTabNavigation: FC<TPageTabNavigation> = (props) => {
|
|||||||
href={`/${workspaceSlug}/projects/${projectId}/pages?type=${tab.key}`}
|
href={`/${workspaceSlug}/projects/${projectId}/pages?type=${tab.key}`}
|
||||||
onClick={(e) => handleTabClick(e, tab.key)}
|
onClick={(e) => handleTabClick(e, tab.key)}
|
||||||
>
|
>
|
||||||
<div>
|
<span
|
||||||
<div
|
className={cn(`block p-3 py-4 text-sm font-medium transition-all`, {
|
||||||
className={cn(`p-3 py-4 text-sm font-medium transition-all`, {
|
|
||||||
"text-custom-primary-100": tab.key === pageType,
|
"text-custom-primary-100": tab.key === pageType,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</div>
|
</span>
|
||||||
<div
|
<div
|
||||||
className={cn(`rounded-t border-t-2 transition-all`, {
|
className={cn(`rounded-t border-t-2 transition-all border-transparent`, {
|
||||||
"border-custom-primary-100": tab.key === pageType,
|
"border-custom-primary-100": tab.key === pageType,
|
||||||
"border-transparent": tab.key !== pageType,
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,7 +22,10 @@ type Props = {
|
|||||||
export const PagesListMainContent: React.FC<Props> = observer((props) => {
|
export const PagesListMainContent: React.FC<Props> = observer((props) => {
|
||||||
const { children, pageType, projectId } = props;
|
const { children, pageType, projectId } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { loader, filteredPageIds, pageIds, filters } = useProjectPages(projectId);
|
const { loader, getCurrentProjectFilteredPageIds, getCurrentProjectPageIds, filters } = useProjectPages(projectId);
|
||||||
|
// derived values
|
||||||
|
const pageIds = getCurrentProjectPageIds(pageType);
|
||||||
|
const filteredPageIds = getCurrentProjectFilteredPageIds(pageType);
|
||||||
|
|
||||||
if (loader === "init-loader") return <PageLoader />;
|
if (loader === "init-loader") return <PageLoader />;
|
||||||
// if no pages exist in the active page type
|
// if no pages exist in the active page type
|
||||||
|
@ -18,10 +18,7 @@ export const PagesListView: React.FC<TPageView> = observer((props) => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const { getAllPages } = useProjectPages(projectId);
|
const { getAllPages } = useProjectPages(projectId);
|
||||||
// fetching pages list
|
// fetching pages list
|
||||||
useSWR(
|
useSWR(projectId ? `PROJECT_PAGES_${projectId}` : null, projectId ? () => getAllPages(pageType) : null);
|
||||||
projectId && pageType ? `PROJECT_PAGES_${projectId}_${pageType}` : null,
|
|
||||||
projectId && pageType ? () => getAllPages(pageType) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
// pages loader
|
// pages loader
|
||||||
return (
|
return (
|
||||||
|
@ -36,7 +36,11 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
|||||||
projectId={projectId.toString()}
|
projectId={projectId.toString()}
|
||||||
pageType={currentPageType()}
|
pageType={currentPageType()}
|
||||||
>
|
>
|
||||||
<PagesListRoot workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} />
|
<PagesListRoot
|
||||||
|
pageType={currentPageType()}
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
/>
|
||||||
</PagesListView>
|
</PagesListView>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import unset from "lodash/unset";
|
import unset from "lodash/unset";
|
||||||
import { makeObservable, observable, runInAction, action, computed } from "mobx";
|
import { makeObservable, observable, runInAction, action } from "mobx";
|
||||||
import { computedFn } from "mobx-utils";
|
import { computedFn } from "mobx-utils";
|
||||||
|
// types
|
||||||
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
||||||
// helpers
|
// helpers
|
||||||
import { filterPagesByPageType, orderPages, shouldFilterPage } from "@/helpers/page.helper";
|
import { filterPagesByPageType, orderPages, shouldFilterPage } from "@/helpers/page.helper";
|
||||||
// services
|
// services
|
||||||
import { PageService } from "@/services/page.service";
|
import { PageService } from "@/services/page.service";
|
||||||
|
// store
|
||||||
import { IPageStore, PageStore } from "@/store/pages/page.store";
|
import { IPageStore, PageStore } from "@/store/pages/page.store";
|
||||||
import { RootStore } from "../root.store";
|
import { RootStore } from "../root.store";
|
||||||
|
|
||||||
@ -17,14 +19,12 @@ type TError = { title: string; description: string };
|
|||||||
export interface IProjectPageStore {
|
export interface IProjectPageStore {
|
||||||
// observables
|
// observables
|
||||||
loader: TLoader;
|
loader: TLoader;
|
||||||
pageType: TPageNavigationTabs;
|
|
||||||
data: Record<string, IPageStore>; // pageId => PageStore
|
data: Record<string, IPageStore>; // pageId => PageStore
|
||||||
error: TError | undefined;
|
error: TError | undefined;
|
||||||
filters: TPageFilters;
|
filters: TPageFilters;
|
||||||
// computed
|
|
||||||
pageIds: string[] | undefined;
|
|
||||||
filteredPageIds: string[] | undefined;
|
|
||||||
// helper actions
|
// helper actions
|
||||||
|
getCurrentProjectPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
||||||
|
getCurrentProjectFilteredPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
||||||
pageById: (pageId: string) => IPageStore | undefined;
|
pageById: (pageId: string) => IPageStore | undefined;
|
||||||
updateFilters: <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => void;
|
updateFilters: <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => void;
|
||||||
clearAllFilters: () => void;
|
clearAllFilters: () => void;
|
||||||
@ -38,7 +38,6 @@ export interface IProjectPageStore {
|
|||||||
export class ProjectPageStore implements IProjectPageStore {
|
export class ProjectPageStore implements IProjectPageStore {
|
||||||
// observables
|
// observables
|
||||||
loader: TLoader = "init-loader";
|
loader: TLoader = "init-loader";
|
||||||
pageType: TPageNavigationTabs = "public";
|
|
||||||
data: Record<string, IPageStore> = {}; // pageId => PageStore
|
data: Record<string, IPageStore> = {}; // pageId => PageStore
|
||||||
error: TError | undefined = undefined;
|
error: TError | undefined = undefined;
|
||||||
filters: TPageFilters = {
|
filters: TPageFilters = {
|
||||||
@ -53,13 +52,9 @@ export class ProjectPageStore implements IProjectPageStore {
|
|||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observables
|
// observables
|
||||||
loader: observable.ref,
|
loader: observable.ref,
|
||||||
pageType: observable.ref,
|
|
||||||
data: observable,
|
data: observable,
|
||||||
error: observable,
|
error: observable,
|
||||||
filters: observable,
|
filters: observable,
|
||||||
// computed
|
|
||||||
pageIds: computed,
|
|
||||||
filteredPageIds: computed,
|
|
||||||
// helper actions
|
// helper actions
|
||||||
updateFilters: action,
|
updateFilters: action,
|
||||||
clearAllFilters: action,
|
clearAllFilters: action,
|
||||||
@ -73,26 +68,35 @@ export class ProjectPageStore implements IProjectPageStore {
|
|||||||
this.service = new PageService();
|
this.service = new PageService();
|
||||||
}
|
}
|
||||||
|
|
||||||
get pageIds() {
|
/**
|
||||||
|
* @description get the current project page ids based on the pageType
|
||||||
|
* @param {TPageNavigationTabs} pageType
|
||||||
|
*/
|
||||||
|
getCurrentProjectPageIds = computedFn((pageType: TPageNavigationTabs) => {
|
||||||
const { projectId } = this.store.app.router;
|
const { projectId } = this.store.app.router;
|
||||||
if (!projectId) return undefined;
|
if (!projectId) return undefined;
|
||||||
|
|
||||||
// helps to filter pages based on the pageType
|
// helps to filter pages based on the pageType
|
||||||
const pagesByType = filterPagesByPageType(this.pageType, Object.values(this?.data || {}));
|
let pagesByType = filterPagesByPageType(pageType, Object.values(this?.data || {}));
|
||||||
|
pagesByType = pagesByType.filter((p) => p.project === projectId);
|
||||||
|
|
||||||
const pages = (pagesByType.map((page) => page.id) as string[]) || undefined;
|
const pages = (pagesByType.map((page) => page.id) as string[]) || undefined;
|
||||||
|
|
||||||
return pages ?? undefined;
|
return pages ?? undefined;
|
||||||
}
|
});
|
||||||
|
|
||||||
get filteredPageIds() {
|
/**
|
||||||
|
* @description get the current project filtered page ids based on the pageType
|
||||||
|
* @param {TPageNavigationTabs} pageType
|
||||||
|
*/
|
||||||
|
getCurrentProjectFilteredPageIds = computedFn((pageType: TPageNavigationTabs) => {
|
||||||
const { projectId } = this.store.app.router;
|
const { projectId } = this.store.app.router;
|
||||||
if (!projectId) return undefined;
|
if (!projectId) return undefined;
|
||||||
|
|
||||||
// helps to filter pages based on the pageType
|
// helps to filter pages based on the pageType
|
||||||
const pagesByType = filterPagesByPageType(this.pageType, Object.values(this?.data || {}));
|
const pagesByType = filterPagesByPageType(pageType, Object.values(this?.data || {}));
|
||||||
let filteredPages = pagesByType.filter(
|
let filteredPages = pagesByType.filter(
|
||||||
(p) =>
|
(p) =>
|
||||||
|
p.project === projectId &&
|
||||||
p.name?.toLowerCase().includes(this.filters.searchQuery.toLowerCase()) &&
|
p.name?.toLowerCase().includes(this.filters.searchQuery.toLowerCase()) &&
|
||||||
shouldFilterPage(p, this.filters.filters)
|
shouldFilterPage(p, this.filters.filters)
|
||||||
);
|
);
|
||||||
@ -101,15 +105,14 @@ export class ProjectPageStore implements IProjectPageStore {
|
|||||||
const pages = (filteredPages.map((page) => page.id) as string[]) || undefined;
|
const pages = (filteredPages.map((page) => page.id) as string[]) || undefined;
|
||||||
|
|
||||||
return pages ?? undefined;
|
return pages ?? undefined;
|
||||||
}
|
|
||||||
|
|
||||||
pageById = computedFn((pageId: string) => {
|
|
||||||
const { projectId } = this.store.app.router;
|
|
||||||
if (!projectId) return undefined;
|
|
||||||
|
|
||||||
return this.data?.[pageId] || undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get the page store by id
|
||||||
|
* @param {string} pageId
|
||||||
|
*/
|
||||||
|
pageById = computedFn((pageId: string) => this.data?.[pageId] || undefined);
|
||||||
|
|
||||||
updateFilters = <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => {
|
updateFilters = <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.filters, [filterKey], filterValue);
|
set(this.filters, [filterKey], filterValue);
|
||||||
@ -125,17 +128,15 @@ export class ProjectPageStore implements IProjectPageStore {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch all the pages based on the navigation tab
|
* @description fetch all the pages
|
||||||
* @param {TPageNavigationTabs} pageType
|
|
||||||
*/
|
*/
|
||||||
getAllPages = async (pageType: TPageNavigationTabs) => {
|
getAllPages = async (pageType: TPageNavigationTabs) => {
|
||||||
try {
|
try {
|
||||||
const { workspaceSlug, projectId } = this.store.app.router;
|
const { workspaceSlug, projectId } = this.store.app.router;
|
||||||
if (!workspaceSlug || !projectId) return undefined;
|
if (!workspaceSlug || !projectId) return undefined;
|
||||||
|
|
||||||
const currentPageIds = this.pageIds;
|
const currentPageIds = this.getCurrentProjectPageIds(pageType);
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.pageType = pageType;
|
|
||||||
this.loader = currentPageIds && currentPageIds.length > 0 ? `mutation-loader` : `init-loader`;
|
this.loader = currentPageIds && currentPageIds.length > 0 ? `mutation-loader` : `init-loader`;
|
||||||
this.error = undefined;
|
this.error = undefined;
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user