mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-1215] chore: display favorite module
filter in applied filter section. (#4402)
* [WEB-1215] chore: display `favorite module` filter in applied filter section. * fix: build errors.
This commit is contained in:
parent
cc4bb385fe
commit
1cbbddb1be
@ -1,5 +1,5 @@
|
|||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { TModuleFilters } from "@plane/types";
|
import { TModuleDisplayFilters, TModuleFilters } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { AppliedDateFilters, AppliedMembersFilters, AppliedStatusFilters } from "@/components/modules";
|
import { AppliedDateFilters, AppliedMembersFilters, AppliedStatusFilters } from "@/components/modules";
|
||||||
// helpers
|
// helpers
|
||||||
@ -8,19 +8,30 @@ import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: TModuleFilters;
|
appliedFilters: TModuleFilters;
|
||||||
|
isFavoriteFilterApplied?: boolean;
|
||||||
handleClearAllFilters: () => void;
|
handleClearAllFilters: () => void;
|
||||||
|
handleDisplayFiltersUpdate?: (updatedDisplayProperties: Partial<TModuleDisplayFilters>) => void;
|
||||||
handleRemoveFilter: (key: keyof TModuleFilters, value: string | null) => void;
|
handleRemoveFilter: (key: keyof TModuleFilters, value: string | null) => void;
|
||||||
alwaysAllowEditing?: boolean;
|
alwaysAllowEditing?: boolean;
|
||||||
|
isArchived?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MEMBERS_FILTERS = ["lead", "members"];
|
const MEMBERS_FILTERS = ["lead", "members"];
|
||||||
const DATE_FILTERS = ["start_date", "target_date"];
|
const DATE_FILTERS = ["start_date", "target_date"];
|
||||||
|
|
||||||
export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, alwaysAllowEditing } = props;
|
const {
|
||||||
|
appliedFilters,
|
||||||
|
isFavoriteFilterApplied,
|
||||||
|
handleClearAllFilters,
|
||||||
|
handleRemoveFilter,
|
||||||
|
handleDisplayFiltersUpdate,
|
||||||
|
alwaysAllowEditing,
|
||||||
|
isArchived = false,
|
||||||
|
} = props;
|
||||||
|
|
||||||
if (!appliedFilters) return null;
|
if (!appliedFilters && !isFavoriteFilterApplied) return null;
|
||||||
if (Object.keys(appliedFilters).length === 0) return null;
|
if (Object.keys(appliedFilters).length === 0 && !isFavoriteFilterApplied) return null;
|
||||||
|
|
||||||
const isEditingAllowed = alwaysAllowEditing;
|
const isEditingAllowed = alwaysAllowEditing;
|
||||||
|
|
||||||
@ -73,6 +84,33 @@ export const ModuleAppliedFiltersList: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{!isArchived && isFavoriteFilterApplied && (
|
||||||
|
<div
|
||||||
|
key="module_display_filters"
|
||||||
|
className="flex flex-wrap items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 capitalize"
|
||||||
|
>
|
||||||
|
<div className="flex flex-wrap items-center gap-1.5">
|
||||||
|
<span className="text-xs text-custom-text-300">Modules</span>
|
||||||
|
<div className="flex items-center gap-1 rounded p-1 text-xs bg-custom-background-80">
|
||||||
|
Favorite
|
||||||
|
{isEditingAllowed && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="grid place-items-center text-custom-text-300 hover:text-custom-text-200"
|
||||||
|
onClick={() =>
|
||||||
|
handleDisplayFiltersUpdate &&
|
||||||
|
handleDisplayFiltersUpdate({
|
||||||
|
favorites: !isFavoriteFilterApplied,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<X size={10} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{isEditingAllowed && (
|
{isEditingAllowed && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -64,6 +64,7 @@ export const ArchivedModuleLayoutRoot: React.FC = observer(() => {
|
|||||||
handleClearAllFilters={() => clearAllFilters(projectId.toString(), "archived")}
|
handleClearAllFilters={() => clearAllFilters(projectId.toString(), "archived")}
|
||||||
handleRemoveFilter={handleRemoveFilter}
|
handleRemoveFilter={handleRemoveFilter}
|
||||||
alwaysAllowEditing
|
alwaysAllowEditing
|
||||||
|
isArchived
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -84,7 +84,7 @@ export const ModuleViewHeader: FC = observer(() => {
|
|||||||
if (isSearchOpen && searchQuery.trim() === "") setIsSearchOpen(false);
|
if (isSearchOpen && searchQuery.trim() === "") setIsSearchOpen(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const isFiltersApplied = calculateTotalFilters(filters ?? {}) !== 0;
|
const isFiltersApplied = calculateTotalFilters(filters ?? {}) !== 0 || displayFilters?.favorites;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hidden h-full sm:flex items-center gap-3 self-end">
|
<div className="hidden h-full sm:flex items-center gap-3 self-end">
|
||||||
|
@ -4,13 +4,7 @@ import { useRouter } from "next/router";
|
|||||||
// components
|
// components
|
||||||
import { ListLayout } from "@/components/core/list";
|
import { ListLayout } from "@/components/core/list";
|
||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
import {
|
import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "@/components/modules";
|
||||||
ModuleCardItem,
|
|
||||||
ModuleListItem,
|
|
||||||
ModulePeekOverview,
|
|
||||||
ModuleViewHeader,
|
|
||||||
ModulesListGanttChartView,
|
|
||||||
} from "@/components/modules";
|
|
||||||
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "@/components/ui";
|
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "@/components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { EmptyStateType } from "@/constants/empty-state";
|
import { EmptyStateType } from "@/constants/empty-state";
|
||||||
@ -73,12 +67,6 @@ export const ModulesListView: React.FC = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="h-[50px] flex-shrink-0 w-full border-b border-custom-border-200 px-6 relative flex items-center gap-4 justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="block text-sm font-medium">Module name</span>
|
|
||||||
</div>
|
|
||||||
<ModuleViewHeader />
|
|
||||||
</div>
|
|
||||||
{displayFilters?.layout === "list" && (
|
{displayFilters?.layout === "list" && (
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
<div className="flex h-full w-full justify-between">
|
<div className="flex h-full w-full justify-between">
|
||||||
|
@ -7,7 +7,7 @@ import { TModuleFilters } from "@plane/types";
|
|||||||
import { PageHead } from "@/components/core";
|
import { PageHead } from "@/components/core";
|
||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
import { ModulesListHeader } from "@/components/headers";
|
import { ModulesListHeader } from "@/components/headers";
|
||||||
import { ModuleAppliedFiltersList, ModulesListView } from "@/components/modules";
|
import { ModuleViewHeader, ModuleAppliedFiltersList, ModulesListView } from "@/components/modules";
|
||||||
// types
|
// types
|
||||||
// hooks
|
// hooks
|
||||||
import ModulesListMobileHeader from "@/components/modules/moduels-list-mobile-header";
|
import ModulesListMobileHeader from "@/components/modules/moduels-list-mobile-header";
|
||||||
@ -22,7 +22,8 @@ const ProjectModulesPage: NextPageWithLayout = observer(() => {
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store
|
// store
|
||||||
const { getProjectById, currentProjectDetails } = useProject();
|
const { getProjectById, currentProjectDetails } = useProject();
|
||||||
const { currentProjectFilters, clearAllFilters, updateFilters } = useModuleFilter();
|
const { currentProjectFilters, currentProjectDisplayFilters, clearAllFilters, updateFilters, updateDisplayFilters } =
|
||||||
|
useModuleFilter();
|
||||||
// derived values
|
// derived values
|
||||||
const project = projectId ? getProjectById(projectId.toString()) : undefined;
|
const project = projectId ? getProjectById(projectId.toString()) : undefined;
|
||||||
const pageTitle = project?.name ? `${project?.name} - Modules` : undefined;
|
const pageTitle = project?.name ? `${project?.name} - Modules` : undefined;
|
||||||
@ -57,12 +58,23 @@ const ProjectModulesPage: NextPageWithLayout = observer(() => {
|
|||||||
<>
|
<>
|
||||||
<PageHead title={pageTitle} />
|
<PageHead title={pageTitle} />
|
||||||
<div className="h-full w-full flex flex-col">
|
<div className="h-full w-full flex flex-col">
|
||||||
{calculateTotalFilters(currentProjectFilters ?? {}) !== 0 && (
|
<div className="h-[50px] flex-shrink-0 w-full border-b border-custom-border-200 px-6 relative flex items-center gap-4 justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="block text-sm font-medium">Module name</span>
|
||||||
|
</div>
|
||||||
|
<ModuleViewHeader />
|
||||||
|
</div>
|
||||||
|
{(calculateTotalFilters(currentProjectFilters ?? {}) !== 0 || currentProjectDisplayFilters?.favorites) && (
|
||||||
<div className="border-b border-custom-border-200 px-5 py-3">
|
<div className="border-b border-custom-border-200 px-5 py-3">
|
||||||
<ModuleAppliedFiltersList
|
<ModuleAppliedFiltersList
|
||||||
appliedFilters={currentProjectFilters ?? {}}
|
appliedFilters={currentProjectFilters ?? {}}
|
||||||
|
isFavoriteFilterApplied={currentProjectDisplayFilters?.favorites ?? false}
|
||||||
handleClearAllFilters={() => clearAllFilters(`${projectId}`)}
|
handleClearAllFilters={() => clearAllFilters(`${projectId}`)}
|
||||||
handleRemoveFilter={handleRemoveFilter}
|
handleRemoveFilter={handleRemoveFilter}
|
||||||
|
handleDisplayFiltersUpdate={(val) => {
|
||||||
|
if (!projectId) return;
|
||||||
|
updateDisplayFilters(projectId.toString(), val);
|
||||||
|
}}
|
||||||
alwaysAllowEditing
|
alwaysAllowEditing
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -177,6 +177,7 @@ export class ModuleFilterStore implements IModuleFilterStore {
|
|||||||
clearAllFilters = (projectId: string, state: keyof TModuleFiltersByState = "default") => {
|
clearAllFilters = (projectId: string, state: keyof TModuleFiltersByState = "default") => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.filters[projectId][state] = {};
|
this.filters[projectId][state] = {};
|
||||||
|
this.displayFilters[projectId].favorites = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user