[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:
Aaryan Khandelwal 2024-04-17 20:32:25 +05:30 committed by GitHub
parent 0c0bdd6969
commit 0d70960e4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 125 additions and 124 deletions

View File

@ -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>
</> </>

View File

@ -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) => {

View File

@ -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}

View File

@ -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={() => {

View File

@ -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 />}
</> </>
); );
}); });

View File

@ -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>
</> </>

View File

@ -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={() => {

View File

@ -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={() => {

View File

@ -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 = "";
} }
); );
} }

View File

@ -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 = "";
} }
); );
} }

View File

@ -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 = "";
}
);
} }
/** /**

View File

@ -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 = "";
} }
); );
} }