From b165e2a3fe955198dbf2ff11a35268663c07f47c Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:39:06 +0530 Subject: [PATCH] [WEB-5614] chore: platform header and breadcrumb enhancements (#8383) --- .../[projectId]/cycles/(detail)/header.tsx | 14 ++-- .../[projectId]/modules/(detail)/header.tsx | 13 +++- .../components/cycles/active-cycle/root.tsx | 9 +-- .../components/cycles/cycles-view-header.tsx | 67 ++++++++++--------- .../cycles/list/cycle-list-item-action.tsx | 2 +- apps/web/core/components/cycles/list/root.tsx | 4 +- .../core/components/cycles/quick-actions.tsx | 10 ++- .../components/modules/module-view-header.tsx | 2 +- .../core/components/modules/quick-actions.tsx | 30 ++++++--- .../rich-filters/filters-toggle.tsx | 35 +++------- .../core/components/views/quick-actions.tsx | 9 ++- .../src/icons/actions/filter-applied-icon.tsx | 21 ++++++ .../propel/src/icons/actions/filter-icon.tsx | 17 +++++ packages/propel/src/icons/actions/index.ts | 2 + packages/propel/src/icons/constants.tsx | 2 + packages/propel/src/icons/registry.ts | 11 ++- 16 files changed, 163 insertions(+), 85 deletions(-) create mode 100644 packages/propel/src/icons/actions/filter-applied-icon.tsx create mode 100644 packages/propel/src/icons/actions/filter-icon.tsx diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/header.tsx index 7e97e57b6..b74886bae 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/header.tsx @@ -14,6 +14,7 @@ import { import { usePlatformOS } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; +import { IconButton } from "@plane/propel/icon-button"; import { CycleIcon } from "@plane/propel/icons"; import { Tooltip } from "@plane/propel/tooltip"; import type { ICustomSearchSelectOption, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types"; @@ -236,7 +237,6 @@ export const CycleIssuesHeader = observer(function CycleIssuesHeader() { + )} - + {moduleId && ( ); } @@ -114,7 +115,7 @@ export const ActiveCycleRoot = observer(function ActiveCycleRoot(props: IActiveC {({ open }) => ( <> - + diff --git a/apps/web/core/components/cycles/cycles-view-header.tsx b/apps/web/core/components/cycles/cycles-view-header.tsx index 9165a895a..b99b7881d 100644 --- a/apps/web/core/components/cycles/cycles-view-header.tsx +++ b/apps/web/core/components/cycles/cycles-view-header.tsx @@ -70,49 +70,50 @@ export const CyclesViewHeader = observer(function CyclesViewHeader(props: Props) }, [searchQuery]); return ( -
- {!isSearchOpen && ( +
+ {!isSearchOpen ? ( { setIsSearchOpen(true); inputRef.current?.focus(); }} icon={Search} /> + ) : ( +
+ + updateSearchQuery(e.target.value)} + onKeyDown={handleInputKeyDown} + /> + {isSearchOpen && ( + + )} +
)} -
- - updateSearchQuery(e.target.value)} - onKeyDown={handleInputKeyDown} - /> - {isSearchOpen && ( - - )} -
+ } title={t("common.filters")} diff --git a/apps/web/core/components/cycles/list/cycle-list-item-action.tsx b/apps/web/core/components/cycles/list/cycle-list-item-action.tsx index 2b0054ecc..c0021d362 100644 --- a/apps/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/apps/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -301,7 +301,7 @@ export const CycleListItemAction = observer(function CycleListItemAction(props: {createdByDetails && !isActive && } {!isActive && ( -
+
{cycleDetails.assignee_ids && cycleDetails.assignee_ids?.length > 0 ? ( {cycleDetails.assignee_ids?.map((assignee_id) => { diff --git a/apps/web/core/components/cycles/list/root.tsx b/apps/web/core/components/cycles/list/root.tsx index ff57f5002..92278ffb4 100644 --- a/apps/web/core/components/cycles/list/root.tsx +++ b/apps/web/core/components/cycles/list/root.tsx @@ -40,7 +40,7 @@ export const CyclesList = observer(function CyclesList(props: ICyclesList) { {({ open }) => ( <> - + {({ open }) => ( <> - + )} - + } + placement="bottom-end" + closeOnSelect + maxHeight="lg" + buttonClassName={customClassName} + > {MENU_ITEMS.map((item) => { if (item.shouldRender === false) return null; return ( diff --git a/apps/web/core/components/modules/module-view-header.tsx b/apps/web/core/components/modules/module-view-header.tsx index 31aa7e4f6..ef2e4e626 100644 --- a/apps/web/core/components/modules/module-view-header.tsx +++ b/apps/web/core/components/modules/module-view-header.tsx @@ -94,7 +94,7 @@ export const ModuleViewHeader = observer(function ModuleViewHeader() { const isFiltersApplied = calculateTotalFilters(filters ?? {}) !== 0 || displayFilters?.favorites; return ( -
+
{!isSearchOpen && ( ({ - ...item, - action: () => { - captureClick({ - elementName: MODULE_TRACKER_ELEMENTS.CONTEXT_MENU, - }); - item.action(); - }, - })); + const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) { + return { + ...item, + + onClick: () => { + captureClick({ + elementName: MODULE_TRACKER_ELEMENTS.CONTEXT_MENU, + }); + item.action(); + }, + }; + }); return ( <> @@ -145,7 +150,12 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
)} - + } + placement="bottom-end" + closeOnSelect + buttonClassName={customClassName} + > {MENU_ITEMS.map((item) => { if (item.shouldRender === false) return null; return ( diff --git a/apps/web/core/components/rich-filters/filters-toggle.tsx b/apps/web/core/components/rich-filters/filters-toggle.tsx index 2f9fb110d..409b5fa84 100644 --- a/apps/web/core/components/rich-filters/filters-toggle.tsx +++ b/apps/web/core/components/rich-filters/filters-toggle.tsx @@ -1,9 +1,10 @@ import { observer } from "mobx-react"; -import { ListFilter } from "lucide-react"; // plane imports +import { IconButton } from "@plane/propel/icon-button"; +import { FilterIcon, FilterAppliedIcon } from "@plane/propel/icons"; +import { cn } from "@plane/utils"; import type { IFilterInstance } from "@plane/shared-state"; import type { TExternalFilter, TFilterProperty } from "@plane/types"; -import { cn } from "@plane/ui"; // components import { AddFilterButton } from "@/components/rich-filters/add-filters/button"; @@ -49,28 +50,14 @@ export const FiltersToggle = observer(function FiltersToggle

-

- - {showFilterRowChangesPill && ( - - )} -
- + className={cn({ + "text-accent-primary bg-accent-subtle border border-accent-subtle-1": showFilterRowChangesPill, + })} + /> ); }); diff --git a/apps/web/core/components/views/quick-actions.tsx b/apps/web/core/components/views/quick-actions.tsx index 59596b80d..dbab8280b 100644 --- a/apps/web/core/components/views/quick-actions.tsx +++ b/apps/web/core/components/views/quick-actions.tsx @@ -1,7 +1,9 @@ import { useState } from "react"; import { observer } from "mobx-react"; +import { MoreHorizontal } from "lucide-react"; // types import { EUserPermissions, EUserPermissionsLevel, PROJECT_VIEW_TRACKER_ELEMENTS } from "@plane/constants"; +import { IconButton } from "@plane/propel/icon-button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { IProjectView } from "@plane/types"; // ui @@ -96,7 +98,12 @@ export const ViewQuickActions = observer(function ViewQuickActions(props: Props) setPublishModalOpen(false)} view={view} /> {additionalModals} - + } + placement="bottom-end" + closeOnSelect + buttonClassName={customClassName} + > {MENU_ITEMS.map((item) => { if (item.shouldRender === false) return null; return ( diff --git a/packages/propel/src/icons/actions/filter-applied-icon.tsx b/packages/propel/src/icons/actions/filter-applied-icon.tsx new file mode 100644 index 000000000..9daf2b11d --- /dev/null +++ b/packages/propel/src/icons/actions/filter-applied-icon.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; + +import { IconWrapper } from "../icon-wrapper"; +import type { ISvgIcons } from "../type"; + +export function FilterAppliedIcon({ color = "text-icon-brand", ...rest }: ISvgIcons) { + const clipPathId = React.useId(); + + return ( + + + + + ); +} diff --git a/packages/propel/src/icons/actions/filter-icon.tsx b/packages/propel/src/icons/actions/filter-icon.tsx new file mode 100644 index 000000000..b0d038e07 --- /dev/null +++ b/packages/propel/src/icons/actions/filter-icon.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; + +import { IconWrapper } from "../icon-wrapper"; +import type { ISvgIcons } from "../type"; + +export function FilterIcon({ color = "currentColor", ...rest }: ISvgIcons) { + const clipPathId = React.useId(); + + return ( + + + + ); +} diff --git a/packages/propel/src/icons/actions/index.ts b/packages/propel/src/icons/actions/index.ts index 429a27a86..a5249e90b 100644 --- a/packages/propel/src/icons/actions/index.ts +++ b/packages/propel/src/icons/actions/index.ts @@ -3,6 +3,8 @@ export * from "./add-icon"; export * from "./add-workitem-icon"; export * from "./add-reaction-icon"; export * from "./close-icon"; +export * from "./filter-icon"; +export * from "./filter-applied-icon"; export * from "./search-icon"; export * from "./preferences-icon"; export * from "./copy-link"; diff --git a/packages/propel/src/icons/constants.tsx b/packages/propel/src/icons/constants.tsx index 6648c7626..1b6778c6d 100644 --- a/packages/propel/src/icons/constants.tsx +++ b/packages/propel/src/icons/constants.tsx @@ -4,6 +4,8 @@ export const ActionsIconsMap = [ { icon: , title: "AddWorkItemIcon" }, { icon: , title: "AddReactionIcon" }, { icon: , title: "CloseIcon" }, + { icon: , title: "FilterIcon" }, + { icon: , title: "FilterAppliedIcon" }, { icon: , title: "SearchIcon" }, { icon: , title: "PreferencesIcon" }, { icon: , title: "CopyLinkIcon" }, diff --git a/packages/propel/src/icons/registry.ts b/packages/propel/src/icons/registry.ts index 6c6a1e849..cea01f88f 100644 --- a/packages/propel/src/icons/registry.ts +++ b/packages/propel/src/icons/registry.ts @@ -1,4 +1,11 @@ -import { AddReactionIcon, AddWorkItemIcon, PreferencesIcon, SearchIcon } from "./actions"; +import { + AddReactionIcon, + AddWorkItemIcon, + FilterAppliedIcon, + FilterIcon, + PreferencesIcon, + SearchIcon, +} from "./actions"; import { AddIcon } from "./actions/add-icon"; import { CloseIcon } from "./actions/close-icon"; import { ChevronDownIcon } from "./arrows/chevron-down"; @@ -121,6 +128,8 @@ export const ICON_REGISTRY = { "action.add-workitem": AddWorkItemIcon, "action.add-reaction": AddReactionIcon, "action.close": CloseIcon, + "action.filter": FilterIcon, + "action.filter-applied": FilterAppliedIcon, "action.search": SearchIcon, "action.preferences": PreferencesIcon, "action.copy-link": CopyLinkIcon,