forked from github/plane
[WEB-1026] chore: clear search query on project change (#4225)
* chore: clear search query on project change * fix: project query search
This commit is contained in:
parent
0c0bdd6969
commit
0d70960e4e
@ -168,15 +168,14 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
</span>
|
</span>
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
)}
|
)}
|
||||||
|
<hr className="my-2 border-custom-border-200" />
|
||||||
{!isCompleted && isEditingAllowed && (
|
{!isCompleted && isEditingAllowed && (
|
||||||
<div className="border-t pt-1 mt-1">
|
<CustomMenu.MenuItem onClick={handleDeleteCycle}>
|
||||||
<CustomMenu.MenuItem onClick={handleDeleteCycle}>
|
<span className="flex items-center justify-start gap-2">
|
||||||
<span className="flex items-center justify-start gap-2">
|
<Trash2 className="h-3 w-3" />
|
||||||
<Trash2 className="h-3 w-3" />
|
<span>Delete cycle</span>
|
||||||
<span>Delete cycle</span>
|
</span>
|
||||||
</span>
|
</CustomMenu.MenuItem>
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</>
|
</>
|
||||||
|
@ -13,7 +13,7 @@ import { FiltersDropdown } from "@/components/issues";
|
|||||||
import { ProjectFiltersSelection, ProjectOrderByDropdown } from "@/components/project";
|
import { ProjectFiltersSelection, ProjectOrderByDropdown } from "@/components/project";
|
||||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { useApplication, useEventTracker, useMember, useProject, useProjectFilter, useUser } from "@/hooks/store";
|
import { useApplication, useEventTracker, useMember, useProjectFilter, useUser } from "@/hooks/store";
|
||||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||||
|
|
||||||
export const ProjectsHeader = observer(() => {
|
export const ProjectsHeader = observer(() => {
|
||||||
@ -30,7 +30,6 @@ export const ProjectsHeader = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { workspaceProjectIds } = useProject();
|
|
||||||
const {
|
const {
|
||||||
currentWorkspaceDisplayFilters: displayFilters,
|
currentWorkspaceDisplayFilters: displayFilters,
|
||||||
currentWorkspaceFilters: filters,
|
currentWorkspaceFilters: filters,
|
||||||
@ -89,52 +88,50 @@ export const ProjectsHeader = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex items-center justify-end gap-3">
|
<div className="w-full flex items-center justify-end gap-3">
|
||||||
{workspaceProjectIds && workspaceProjectIds?.length > 0 && (
|
<div className="flex items-center">
|
||||||
<div className="flex items-center">
|
{!isSearchOpen && (
|
||||||
{!isSearchOpen && (
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
className="-mr-1 p-2 hover:bg-custom-background-80 rounded text-custom-text-400 grid place-items-center"
|
||||||
className="-mr-1 p-2 hover:bg-custom-background-80 rounded text-custom-text-400 grid place-items-center"
|
onClick={() => {
|
||||||
onClick={() => {
|
setIsSearchOpen(true);
|
||||||
setIsSearchOpen(true);
|
inputRef.current?.focus();
|
||||||
inputRef.current?.focus();
|
}}
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Search className="h-3.5 w-3.5" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"ml-auto flex items-center justify-start gap-1 rounded-md border border-transparent bg-custom-background-100 text-custom-text-400 w-0 transition-[width] ease-linear overflow-hidden opacity-0",
|
|
||||||
{
|
|
||||||
"w-64 px-2.5 py-1.5 border-custom-border-200 opacity-100": isSearchOpen,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Search className="h-3.5 w-3.5" />
|
<Search className="h-3.5 w-3.5" />
|
||||||
<input
|
</button>
|
||||||
ref={inputRef}
|
)}
|
||||||
className="w-full max-w-[234px] border-none bg-transparent text-sm text-custom-text-100 focus:outline-none"
|
<div
|
||||||
placeholder="Search"
|
className={cn(
|
||||||
value={searchQuery}
|
"ml-auto flex items-center justify-start gap-1 rounded-md border border-transparent bg-custom-background-100 text-custom-text-400 w-0 transition-[width] ease-linear overflow-hidden opacity-0",
|
||||||
onChange={(e) => updateSearchQuery(e.target.value)}
|
{
|
||||||
onKeyDown={handleInputKeyDown}
|
"w-64 px-2.5 py-1.5 border-custom-border-200 opacity-100": isSearchOpen,
|
||||||
/>
|
}
|
||||||
{isSearchOpen && (
|
)}
|
||||||
<button
|
>
|
||||||
type="button"
|
<Search className="h-3.5 w-3.5" />
|
||||||
className="grid place-items-center"
|
<input
|
||||||
onClick={() => {
|
ref={inputRef}
|
||||||
updateSearchQuery("");
|
className="w-full max-w-[234px] border-none bg-transparent text-sm text-custom-text-100 placeholder:text-custom-text-400 focus:outline-none"
|
||||||
setIsSearchOpen(false);
|
placeholder="Search"
|
||||||
}}
|
value={searchQuery}
|
||||||
>
|
onChange={(e) => updateSearchQuery(e.target.value)}
|
||||||
<X className="h-3 w-3" />
|
onKeyDown={handleInputKeyDown}
|
||||||
</button>
|
/>
|
||||||
)}
|
{isSearchOpen && (
|
||||||
</div>
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid place-items-center"
|
||||||
|
onClick={() => {
|
||||||
|
updateSearchQuery("");
|
||||||
|
setIsSearchOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
<ProjectOrderByDropdown
|
<ProjectOrderByDropdown
|
||||||
value={displayFilters?.order_by}
|
value={displayFilters?.order_by}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
|
@ -42,7 +42,7 @@ export const InboxIssueOrderByDropdown: FC = observer(() => {
|
|||||||
{inboxSorting?.order_by?.includes(option.key) && <Check className="h-3 w-3" />}
|
{inboxSorting?.order_by?.includes(option.key) && <Check className="h-3 w-3" />}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
<hr className="my-2" />
|
<hr className="my-2 border-custom-border-200" />
|
||||||
{INBOX_ISSUE_SORT_BY_OPTIONS.map((option) => (
|
{INBOX_ISSUE_SORT_BY_OPTIONS.map((option) => (
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
key={option.key}
|
key={option.key}
|
||||||
|
@ -46,7 +46,7 @@ export const ModuleOrderByDropdown: React.FC<Props> = (props) => {
|
|||||||
{value?.includes(option.key) && <Check className="h-3 w-3" />}
|
{value?.includes(option.key) && <Check className="h-3 w-3" />}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
<hr className="my-2" />
|
<hr className="my-2 border-custom-border-200" />
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
className="flex items-center justify-between gap-2"
|
className="flex items-center justify-between gap-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// hooks
|
|
||||||
// components
|
// components
|
||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "@/components/modules";
|
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "@/components/modules";
|
||||||
// ui
|
|
||||||
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "@/components/ui";
|
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "@/components/ui";
|
||||||
// assets
|
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
// hooks
|
||||||
import { useApplication, useEventTracker, useModule, useModuleFilter } from "@/hooks/store";
|
import { useApplication, useEventTracker, useModule, useModuleFilter } from "@/hooks/store";
|
||||||
import AllFiltersImage from "public/empty-state/module/all-filters.svg";
|
import AllFiltersImage from "public/empty-state/module/all-filters.svg";
|
||||||
import NameFilterImage from "public/empty-state/module/name-filter.svg";
|
import NameFilterImage from "public/empty-state/module/name-filter.svg";
|
||||||
@ -22,14 +19,13 @@ export const ModulesListView: React.FC = observer(() => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const { commandPalette: commandPaletteStore } = useApplication();
|
const { commandPalette: commandPaletteStore } = useApplication();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
const { getFilteredModuleIds, loader } = useModule();
|
const { getProjectModuleIds, getFilteredModuleIds, loader } = useModule();
|
||||||
const { currentProjectDisplayFilters: displayFilters, searchQuery, currentProjectFilters } = useModuleFilter();
|
const { currentProjectDisplayFilters: displayFilters, searchQuery } = useModuleFilter();
|
||||||
// derived values
|
// derived values
|
||||||
|
const projectModuleIds = projectId ? getProjectModuleIds(projectId.toString()) : undefined;
|
||||||
const filteredModuleIds = projectId ? getFilteredModuleIds(projectId.toString()) : undefined;
|
const filteredModuleIds = projectId ? getFilteredModuleIds(projectId.toString()) : undefined;
|
||||||
|
|
||||||
const totalFilters = calculateTotalFilters(currentProjectFilters ?? {});
|
if (loader || !projectModuleIds || !filteredModuleIds)
|
||||||
|
|
||||||
if (loader || !filteredModuleIds)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{displayFilters?.layout === "list" && <CycleModuleListLayout />}
|
{displayFilters?.layout === "list" && <CycleModuleListLayout />}
|
||||||
@ -38,7 +34,18 @@ export const ModulesListView: React.FC = observer(() => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (totalFilters > 0 && filteredModuleIds.length === 0)
|
if (projectModuleIds.length === 0)
|
||||||
|
return (
|
||||||
|
<EmptyState
|
||||||
|
type={EmptyStateType.PROJECT_MODULE}
|
||||||
|
primaryButtonOnClick={() => {
|
||||||
|
setTrackElement("Module empty state");
|
||||||
|
commandPaletteStore.toggleCreateModuleModal(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredModuleIds.length === 0)
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full grid place-items-center">
|
<div className="h-full w-full grid place-items-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
@ -59,55 +66,43 @@ export const ModulesListView: React.FC = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filteredModuleIds.length > 0 ? (
|
{displayFilters?.layout === "list" && (
|
||||||
<>
|
<div className="h-full overflow-y-auto">
|
||||||
{displayFilters?.layout === "list" && (
|
<div className="flex h-full w-full justify-between">
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="flex h-full w-full flex-col overflow-y-auto vertical-scrollbar scrollbar-lg">
|
||||||
<div className="flex h-full w-full justify-between">
|
{filteredModuleIds.map((moduleId) => (
|
||||||
<div className="flex h-full w-full flex-col overflow-y-auto vertical-scrollbar scrollbar-lg">
|
<ModuleListItem key={moduleId} moduleId={moduleId} />
|
||||||
{filteredModuleIds.map((moduleId) => (
|
))}
|
||||||
<ModuleListItem key={moduleId} moduleId={moduleId} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<ModulePeekOverview
|
|
||||||
projectId={projectId?.toString() ?? ""}
|
|
||||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<ModulePeekOverview
|
||||||
{displayFilters?.layout === "board" && (
|
projectId={projectId?.toString() ?? ""}
|
||||||
<div className="h-full w-full">
|
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||||
<div className="flex h-full w-full justify-between">
|
/>
|
||||||
<div
|
</div>
|
||||||
className={`grid h-full w-full grid-cols-1 gap-6 overflow-y-auto p-8 ${
|
</div>
|
||||||
peekModule
|
|
||||||
? "lg:grid-cols-1 xl:grid-cols-2 3xl:grid-cols-3"
|
|
||||||
: "lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4"
|
|
||||||
} auto-rows-max transition-all vertical-scrollbar scrollbar-lg`}
|
|
||||||
>
|
|
||||||
{filteredModuleIds.map((moduleId) => (
|
|
||||||
<ModuleCardItem key={moduleId} moduleId={moduleId} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<ModulePeekOverview
|
|
||||||
projectId={projectId?.toString() ?? ""}
|
|
||||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{displayFilters?.layout === "gantt" && <ModulesListGanttChartView />}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<EmptyState
|
|
||||||
type={EmptyStateType.PROJECT_MODULE}
|
|
||||||
primaryButtonOnClick={() => {
|
|
||||||
setTrackElement("Module empty state");
|
|
||||||
commandPaletteStore.toggleCreateModuleModal(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
{displayFilters?.layout === "board" && (
|
||||||
|
<div className="h-full w-full">
|
||||||
|
<div className="flex h-full w-full justify-between">
|
||||||
|
<div
|
||||||
|
className={`grid h-full w-full grid-cols-1 gap-6 overflow-y-auto p-8 ${
|
||||||
|
peekModule
|
||||||
|
? "lg:grid-cols-1 xl:grid-cols-2 3xl:grid-cols-3"
|
||||||
|
: "lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4"
|
||||||
|
} auto-rows-max transition-all vertical-scrollbar scrollbar-lg`}
|
||||||
|
>
|
||||||
|
{filteredModuleIds.map((moduleId) => (
|
||||||
|
<ModuleCardItem key={moduleId} moduleId={moduleId} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<ModulePeekOverview
|
||||||
|
projectId={projectId?.toString() ?? ""}
|
||||||
|
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{displayFilters?.layout === "gantt" && <ModulesListGanttChartView />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -163,15 +163,14 @@ export const ModuleQuickActions: React.FC<Props> = observer((props) => {
|
|||||||
</span>
|
</span>
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
)}
|
)}
|
||||||
|
<hr className="my-2 border-custom-border-200" />
|
||||||
{isEditingAllowed && (
|
{isEditingAllowed && (
|
||||||
<div className="border-t pt-1 mt-1">
|
<CustomMenu.MenuItem onClick={handleDeleteModule}>
|
||||||
<CustomMenu.MenuItem onClick={handleDeleteModule}>
|
<span className="flex items-center justify-start gap-2">
|
||||||
<span className="flex items-center justify-start gap-2">
|
<Trash2 className="h-3 w-3" />
|
||||||
<Trash2 className="h-3 w-3" />
|
<span>Delete module</span>
|
||||||
<span>Delete module</span>
|
</span>
|
||||||
</span>
|
</CustomMenu.MenuItem>
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
</>
|
</>
|
||||||
|
@ -47,7 +47,7 @@ export const PageOrderByDropdown: React.FC<Props> = (props) => {
|
|||||||
{sortKey === option.key && <Check className="h-3 w-3" />}
|
{sortKey === option.key && <Check className="h-3 w-3" />}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
<hr className="my-2" />
|
<hr className="my-2 border-custom-border-200" />
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
className="flex items-center justify-between gap-2"
|
className="flex items-center justify-between gap-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -49,7 +49,7 @@ export const ProjectOrderByDropdown: React.FC<Props> = (props) => {
|
|||||||
{value?.includes(option.key) && <Check className="h-3 w-3" />}
|
{value?.includes(option.key) && <Check className="h-3 w-3" />}
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
<hr className="my-2" />
|
<hr className="my-2 border-custom-border-200" />
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
className="flex items-center justify-between gap-2"
|
className="flex items-center justify-between gap-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -63,6 +63,7 @@ export class CycleFilterStore implements ICycleFilterStore {
|
|||||||
(projectId) => {
|
(projectId) => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
this.initProjectCycleFilters(projectId);
|
this.initProjectCycleFilters(projectId);
|
||||||
|
this.searchQuery = "";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ export class ModuleFilterStore implements IModuleFilterStore {
|
|||||||
(projectId) => {
|
(projectId) => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
this.initProjectModuleFilters(projectId);
|
this.initProjectModuleFilters(projectId);
|
||||||
|
this.searchQuery = "";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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 } from "mobx";
|
import { makeObservable, observable, runInAction, action, reaction } from "mobx";
|
||||||
import { computedFn } from "mobx-utils";
|
import { computedFn } from "mobx-utils";
|
||||||
// types
|
// types
|
||||||
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
||||||
@ -64,8 +64,16 @@ export class ProjectPageStore implements IProjectPageStore {
|
|||||||
createPage: action,
|
createPage: action,
|
||||||
removePage: action,
|
removePage: action,
|
||||||
});
|
});
|
||||||
|
// service
|
||||||
this.service = new PageService();
|
this.service = new PageService();
|
||||||
|
// initialize display filters of the current project
|
||||||
|
reaction(
|
||||||
|
() => this.store.app.router.projectId,
|
||||||
|
(projectId) => {
|
||||||
|
if (!projectId) return;
|
||||||
|
this.filters.searchQuery = "";
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +59,7 @@ export class ProjectFilterStore implements IProjectFilterStore {
|
|||||||
(workspaceSlug) => {
|
(workspaceSlug) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
this.initWorkspaceFilters(workspaceSlug);
|
this.initWorkspaceFilters(workspaceSlug);
|
||||||
|
this.searchQuery = "";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user