mirror of
https://github.com/makeplane/plane.git
synced 2026-07-13 14:01:45 +02:00
fix: projects scope dropdown (#1071)
This commit is contained in:
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { cn } from "@plane/editor";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
import { CustomMenu, Loader } from "@plane/ui";
|
||||
// plane web constants
|
||||
import { PROJECT_SCOPES } from "@/plane-web/constants/project";
|
||||
// plane web hooks
|
||||
@@ -25,14 +25,13 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
|
||||
const router = useRouter();
|
||||
|
||||
// derived values
|
||||
const selectedScope = filters?.scope || EProjectScope.ALL_PROJECTS;
|
||||
const selectedScopeCount = scopeProjectsCount?.[selectedScope];
|
||||
const selectedScope = filters?.scope;
|
||||
const selectedScopeCount = selectedScope && scopeProjectsCount?.[selectedScope];
|
||||
|
||||
const DropdownLabel = () => (
|
||||
<>
|
||||
<div className="hidden md:flex relative items-center gap-2 w-[150px]">
|
||||
<div className="flex gap-2 flex-1 my-auto">
|
||||
{" "}
|
||||
<div className="whitespace-nowrap font-medium my-auto">
|
||||
{(PROJECT_SCOPES || []).find((scope) => selectedScope === scope.key)?.label}
|
||||
</div>
|
||||
@@ -48,7 +47,6 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const DropdownOptions = () =>
|
||||
(PROJECT_SCOPES || []).map((scope) => (
|
||||
<CustomMenu.MenuItem
|
||||
@@ -72,7 +70,7 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
|
||||
</CustomMenu.MenuItem>
|
||||
));
|
||||
|
||||
return (
|
||||
return selectedScope ? (
|
||||
<CustomMenu
|
||||
maxHeight={"md"}
|
||||
className={cn(
|
||||
@@ -86,5 +84,9 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
|
||||
>
|
||||
<DropdownOptions />
|
||||
</CustomMenu>
|
||||
) : (
|
||||
<Loader>
|
||||
<Loader.Item width="150px" height="32px" />
|
||||
</Loader>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useParams, usePathname } from "next/navigation";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
// hooks
|
||||
import { useWorkspace } from "@/hooks/store";
|
||||
import { useProjectFilter } from "@/plane-web/hooks/store";
|
||||
import { ProjectScopeDropdown } from "./dropdowns";
|
||||
import { ProjectAttributesDropdown, ProjectDisplayFiltersDropdown, ProjectLayoutSelection } from "./header";
|
||||
|
||||
@@ -12,9 +13,11 @@ export const ProjectsListMobileHeader = observer(() => {
|
||||
const { workspaceSlug } = useParams();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const pathname = usePathname();
|
||||
const { filters } = useProjectFilter();
|
||||
|
||||
const workspaceId = currentWorkspace?.id || undefined;
|
||||
const isArchived = pathname.includes("/archives");
|
||||
const selectedScope = filters?.scope;
|
||||
|
||||
const customButton = (label: string) => (
|
||||
<div className="flex text-sm items-center gap-2 neutral-primary text-custom-text-200">
|
||||
@@ -30,7 +33,7 @@ export const ProjectsListMobileHeader = observer(() => {
|
||||
<ProjectLayoutSelection workspaceSlug={workspaceSlug.toString()} />
|
||||
</div>
|
||||
)}
|
||||
{!isArchived && (
|
||||
{!isArchived && selectedScope && (
|
||||
<div className="border-l border-custom-border-200 flex justify-around w-full">
|
||||
<ProjectScopeDropdown workspaceSlug={workspaceSlug.toString()} className={"border-none"} />
|
||||
</div>
|
||||
|
||||
@@ -4,10 +4,11 @@ import { FC, useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane web components
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import { useProject } from "@/hooks/store";
|
||||
import { ProjectLayoutRoot } from "@/plane-web/components/projects";
|
||||
// plane web hooks
|
||||
import { useProjectFilter } from "@/plane-web/hooks/store";
|
||||
import { EProjectScope } from "@/plane-web/types/workspace-project-filters";
|
||||
import { EProjectFilters, EProjectScope } from "@/plane-web/types/workspace-project-filters";
|
||||
|
||||
type TWorkspaceProjectsRoot = {
|
||||
workspaceSlug: string;
|
||||
@@ -21,18 +22,23 @@ export const WorkspaceProjectsRoot: FC<TWorkspaceProjectsRoot> = observer((props
|
||||
const searchParams = useSearchParams();
|
||||
const showAllProjects = searchParams.get("show-all-projects");
|
||||
const pathname = usePathname();
|
||||
const { loader } = useProject();
|
||||
|
||||
useEffect(() => {
|
||||
if (workspaceSlug)
|
||||
if (workspaceSlug) {
|
||||
let filtersToInit = [EProjectFilters.LAYOUT, EProjectFilters.ATTRIBUTES, EProjectFilters.DISPLAY_FILTERS];
|
||||
filtersToInit = loader ? filtersToInit : [EProjectFilters.SCOPE, ...filtersToInit];
|
||||
initWorkspaceFilters(
|
||||
workspaceSlug,
|
||||
typeof showAllProjects === "string"
|
||||
? showAllProjects === "true"
|
||||
? EProjectScope.ALL_PROJECTS
|
||||
: EProjectScope.MY_PROJECTS
|
||||
: undefined
|
||||
: undefined,
|
||||
filtersToInit
|
||||
);
|
||||
}, [workspaceSlug, initWorkspaceFilters, pathname, showAllProjects]);
|
||||
}
|
||||
}, [workspaceSlug, initWorkspaceFilters, pathname, showAllProjects, loader]);
|
||||
|
||||
return <ProjectLayoutRoot />;
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import { IProjectFilterHelper, ProjectFilterHelper } from "@/plane-web/store/wor
|
||||
// plane web types
|
||||
import { TProject } from "@/plane-web/types/projects";
|
||||
import {
|
||||
EProjectFilters,
|
||||
EProjectLayouts,
|
||||
EProjectScope,
|
||||
TProjectAttributes,
|
||||
@@ -39,7 +40,7 @@ export interface IProjectFilterStore extends IProjectFilterHelper {
|
||||
) => TProjectsLayoutStructure[T] | undefined;
|
||||
// helpers actions
|
||||
// actions
|
||||
initWorkspaceFilters: (workspaceSlug: string, scope?: EProjectScope) => void;
|
||||
initWorkspaceFilters: (workspaceSlug: string, scope?: EProjectScope, filtersToInit?: EProjectFilters[]) => void;
|
||||
updateScope: (workspaceSlug: string, scope: TProjectScope, setLocalStorage?: boolean) => void;
|
||||
updateLayout: (workspaceSlug: string, layout: TProjectLayouts, setLocalStorage?: boolean) => void;
|
||||
updateAttributes: <T extends keyof TProjectAttributes>(
|
||||
@@ -205,28 +206,29 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { void }
|
||||
*/
|
||||
initWorkspaceFilters = (workspaceSlug: string, scope?: EProjectScope): void => {
|
||||
initWorkspaceFilters = (workspaceSlug: string, scope?: EProjectScope, filtersToInit?: EProjectFilters[]): void => {
|
||||
const savedFilters = this.handleProjectLocalFilters.get(workspaceSlug);
|
||||
|
||||
this.updateScope(
|
||||
workspaceSlug,
|
||||
scope ||
|
||||
(this.scopeProjectsCount[EProjectScope.MY_PROJECTS] > 0
|
||||
? EProjectScope.MY_PROJECTS
|
||||
: EProjectScope.ALL_PROJECTS)
|
||||
);
|
||||
filtersToInit?.includes(EProjectFilters.SCOPE) &&
|
||||
this.updateScope(
|
||||
workspaceSlug,
|
||||
scope ||
|
||||
(this.scopeProjectsCount[EProjectScope.MY_PROJECTS] > 0
|
||||
? EProjectScope.MY_PROJECTS
|
||||
: EProjectScope.ALL_PROJECTS)
|
||||
);
|
||||
|
||||
if (!this.layoutMap[workspaceSlug]) {
|
||||
if (!this.layoutMap[workspaceSlug] && filtersToInit?.includes(EProjectFilters.LAYOUT)) {
|
||||
this.updateLayout(workspaceSlug, savedFilters?.layout || EProjectLayouts.GALLERY, false);
|
||||
}
|
||||
if (!this.attributesMap[workspaceSlug]) {
|
||||
if (!this.attributesMap[workspaceSlug] && filtersToInit?.includes(EProjectFilters.ATTRIBUTES)) {
|
||||
this.updateAttributes(workspaceSlug, "priority", savedFilters?.attributes?.priority || [], false);
|
||||
this.updateAttributes(workspaceSlug, "state", savedFilters?.attributes?.state || [], false);
|
||||
this.updateAttributes(workspaceSlug, "lead", savedFilters?.attributes?.lead || [], false);
|
||||
this.updateAttributes(workspaceSlug, "members", savedFilters?.attributes?.members || [], false);
|
||||
this.updateAttributes(workspaceSlug, "access", savedFilters?.attributes?.access || [], false);
|
||||
}
|
||||
if (!this.displayFiltersMap[workspaceSlug]) {
|
||||
if (!this.displayFiltersMap[workspaceSlug] && filtersToInit?.includes(EProjectFilters.DISPLAY_FILTERS)) {
|
||||
this.updateDisplayFilters(workspaceSlug, "group_by", savedFilters?.display_filters?.group_by || "states", false);
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_by", savedFilters?.display_filters?.sort_by || "manual", false);
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_order", savedFilters?.display_filters?.sort_order || "asc", false);
|
||||
|
||||
@@ -59,6 +59,12 @@ export type TProjectFilters = {
|
||||
attributes: TProjectAttributes;
|
||||
display_filters: TProjectDisplayFilters;
|
||||
};
|
||||
export enum EProjectFilters {
|
||||
SCOPE = "scope",
|
||||
LAYOUT = "layout",
|
||||
ATTRIBUTES = "attributes",
|
||||
DISPLAY_FILTERS = "display_filters",
|
||||
}
|
||||
|
||||
// project_is structure based on the display filter group_by
|
||||
export type TProjectsBoardLayoutStructure = {
|
||||
|
||||
Reference in New Issue
Block a user