From 305357478e0afdee72dae0a7ab88d5213aa4f24b Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 22 Apr 2026 13:32:39 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20=D0=BC?= =?UTF-8?q?=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20mobile=20header,?= =?UTF-8?q?=20intake=20actions=20=D0=B8=20member=20filters=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20=D0=BA=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(projects)/profile/[userId]/header.tsx | 31 ++-- .../profile/[userId]/mobile-header.tsx | 47 ++--- .../cycles/(detail)/mobile-header.tsx | 50 ++---- .../cycles/(list)/mobile-header.tsx | 45 ++--- .../modules/(detail)/mobile-header.tsx | 48 ++--- .../modules/(list)/mobile-header.tsx | 41 ++--- .../inbox/content/inbox-issue-header.tsx | 101 ++++++----- .../content/inbox-issue-mobile-header.tsx | 170 +++++++++--------- .../project/dropdowns/filters/member-list.tsx | 18 +- 9 files changed, 236 insertions(+), 315 deletions(-) diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx index 296b780..79117f9 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx @@ -12,9 +12,10 @@ import { PROFILE_VIEWER_TAB, PROFILE_ADMINS_TAB, EUserPermissions, EUserPermissi import { useTranslation } from "@plane/i18n"; import { YourWorkIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { IUserProfileProjectSegregation } from "@plane/types"; -import { Breadcrumbs, Header, CustomMenu } from "@plane/ui"; +import { Breadcrumbs, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common/breadcrumb-link"; +import { SelectionDropdown } from "@/components/common/selection-dropdown"; import { ProfileIssuesFilter } from "@/components/profile/profile-issues-filter"; // hooks import { useAppTheme } from "@/hooks/store/use-app-theme"; @@ -71,30 +72,22 @@ export const UserProfileHeader = observer(function UserProfileHeader(props: TUse
{showProfileIssuesFilter && }
- {type}
} - customButtonClassName="flex flex-grow justify-center text-secondary text-13" - closeOnSelect - > - <> - {tabsList.map((tab) => ( - router.push(`/${workspaceSlug}/profile/${userId}/${tab.route}`)} - > - {t(tab.i18n_label)} - - ))} - + menuButtonWrapperClassName="flex flex-grow justify-center text-secondary text-13" + options={tabsList.map((tab) => ({ + key: tab.route, + title: t(tab.i18n_label), + isChecked: type === tab.route, + onClick: () => router.push(`/${workspaceSlug}/profile/${userId}/${tab.route}`), + }))} + />
- } - customButtonClassName="flex flex-center text-secondary text-13" - closeOnSelect - > - {ISSUE_LAYOUTS.map((layout, index) => { - if (layout.key === "spreadsheet" || layout.key === "gantt_chart" || layout.key === "calendar") return; - return ( - { - handleLayoutChange(ISSUE_LAYOUTS[index].key); - }} - className="flex items-center gap-2" - > - -
{t(layout.i18n_title)}
-
- ); - })} - +
- {t("common.layout")} - } - customButtonClassName="flex flex-grow justify-center text-secondary text-13" - closeOnSelect - > - {SUPPORTED_LAYOUTS.map((layout, index) => ( - { - handleLayoutChange(ISSUE_LAYOUTS[index].key); - }} - className="flex items-center gap-2" - > - -
{t(layout.titleTranslationKey)}
-
- ))} -
+
- Layout } - customButtonClassName="flex flex-grow justify-center items-center text-secondary text-13" - closeOnSelect - > - {CYCLE_VIEW_LAYOUTS.map((layout) => { - if (layout.key == "gantt") return; - return ( - { - updateDisplayFilters(currentProjectDetails!.id, { - layout: layout.key, - }); - }} - className="flex items-center gap-2" - > - -
{layout.title}
-
- ); - })} -
+ menuButtonWrapperClassName="flex flex-grow justify-center items-center border-b border-subtle bg-surface-1 py-2 text-secondary text-13" + options={CYCLE_VIEW_LAYOUTS.filter((layout) => layout.key !== "gantt").map((layout) => ({ + key: layout.key, + icon: , + title: layout.title, + isChecked: currentProjectDisplayFilters?.layout === layout.key, + onClick: () => + updateDisplayFilters(currentProjectDetails!.id, { + layout: layout.key, + }), + }))} + />
); }); diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx index 317a7b0..70ef213 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx @@ -8,27 +8,23 @@ import { useCallback, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // plane imports -import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; +import { EIssueFilterType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon, ChevronDownIcon } from "@plane/propel/icons"; -import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, EIssueLayoutTypes } from "@plane/types"; -import { EIssuesStoreType } from "@plane/types"; -import { CustomMenu } from "@plane/ui"; +import { ChevronDownIcon } from "@plane/propel/icons"; +import type { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types"; +import { EIssueLayoutTypes, EIssuesStoreType } from "@plane/types"; // components import { WorkItemsModal } from "@/components/analytics/work-items/modal"; -import { DisplayFiltersSelection, FiltersDropdown } from "@/components/issues/issue-layouts/filters"; -import { IssueLayoutIcon } from "@/components/issues/issue-layouts/layout-icon"; +import { + DisplayFiltersSelection, + FiltersDropdown, + MobileLayoutSelection, +} from "@/components/issues/issue-layouts/filters"; // hooks import { useIssues } from "@/hooks/store/use-issues"; import { useModule } from "@/hooks/store/use-module"; import { useProject } from "@/hooks/store/use-project"; -const SUPPORTED_LAYOUTS = [ - { key: "list", i18n_title: "issue.layouts.list", icon: ListLayoutIcon }, - { key: "kanban", i18n_title: "issue.layouts.kanban", icon: BoardLayoutIcon }, - { key: "calendar", i18n_title: "issue.layouts.calendar", icon: CalendarLayoutIcon }, -]; - export const ModuleIssuesMobileHeader = observer(function ModuleIssuesMobileHeader() { // router const { workspaceSlug, projectId, moduleId } = useParams(); @@ -79,27 +75,11 @@ export const ModuleIssuesMobileHeader = observer(function ModuleIssuesMobileHead projectDetails={currentProjectDetails} />
- Layout} - customButtonClassName="flex flex-grow justify-center text-secondary text-13" - closeOnSelect - > - {SUPPORTED_LAYOUTS.map((layout, index) => ( - { - handleLayoutChange(ISSUE_LAYOUTS[index].key); - }} - className="flex items-center gap-2" - > - -
{t(layout.i18n_title)}
-
- ))} -
+
- Layout } - customButtonClassName="flex flex-grow justify-center items-center text-secondary text-13" - closeOnSelect - > - {MODULE_VIEW_LAYOUTS.map((layout) => { - if (layout.key == "gantt") return; - return ( - { - updateDisplayFilters(currentProjectDetails!.id.toString(), { layout: layout.key }); - }} - className="flex items-center gap-2" - > - -
{t(layout.i18n_title)}
-
- ); - })} -
+ menuButtonWrapperClassName="flex flex-grow justify-center items-center border-b border-subtle bg-surface-1 py-2 text-secondary text-13" + options={MODULE_VIEW_LAYOUTS.filter((layout) => layout.key !== "gantt").map((layout) => ({ + key: layout.key, + icon: , + title: t(layout.i18n_title), + isChecked: currentProjectDisplayFilters?.layout === layout.key, + onClick: () => updateDisplayFilters(currentProjectDetails!.id.toString(), { layout: layout.key }), + }))} + />
); }); diff --git a/plane-src/apps/web/core/components/inbox/content/inbox-issue-header.tsx b/plane-src/apps/web/core/components/inbox/content/inbox-issue-header.tsx index 4e36a3e..a07642e 100644 --- a/plane-src/apps/web/core/components/inbox/content/inbox-issue-header.tsx +++ b/plane-src/apps/web/core/components/inbox/content/inbox-issue-header.tsx @@ -25,7 +25,7 @@ import { import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TNameDescriptionLoader } from "@plane/types"; import { EInboxIssueStatus } from "@plane/types"; -import { ControlLink, CustomMenu } from "@plane/ui"; +import { ActionDropdown, ControlLink } from "@plane/ui"; import { copyUrlToClipboard, findHowManyDaysLeft, generateWorkItemLink } from "@plane/utils"; // components import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal"; @@ -348,61 +348,68 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader ) : ( <> {isAllowed && ( - } - customButtonClassName="nodedc-external-icon-button" + } + buttonClassName="nodedc-external-icon-button" placement="bottom-start" - menuItemsClassName="z-[760]" - > - {canMarkAsAccepted && ( - + items={[ + { + key: "snooze", + shouldRender: canMarkAsAccepted, + action: () => handleActionWithPermission( isProjectAdmin, handleIssueSnoozeAction, t("inbox_issue.errors.snooze_permission") - ) - } - > -
- - {inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0 - ? t("inbox_issue.actions.unsnooze") - : t("inbox_issue.actions.snooze")} -
-
- )} - {canMarkAsDuplicate && ( - + ), + customContent: ( +
+ + {inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0 + ? t("inbox_issue.actions.unsnooze") + : t("inbox_issue.actions.snooze")} +
+ ), + }, + { + key: "duplicate", + shouldRender: canMarkAsDuplicate, + action: () => handleActionWithPermission( isProjectAdmin, () => setSelectDuplicateIssue(true), t("inbox_issue.errors.duplicate_permission") - ) - } - > -
- - {t("inbox_issue.actions.mark_as_duplicate")} -
-
- )} - handleCopyIssueLink(workItemLink)}> -
- - {t("inbox_issue.actions.copy")} -
-
- {canDelete && ( - setDeleteIssueModal(true)}> -
- - {t("inbox_issue.actions.delete")} -
-
- )} -
+ ), + customContent: ( +
+ + {t("inbox_issue.actions.mark_as_duplicate")} +
+ ), + }, + { + key: "copy", + action: () => handleCopyIssueLink(workItemLink), + customContent: ( +
+ + {t("inbox_issue.actions.copy")} +
+ ), + }, + { + key: "delete", + shouldRender: canDelete, + action: () => setDeleteIssueModal(true), + customContent: ( +
+ + {t("inbox_issue.actions.delete")} +
+ ), + }, + ]} + /> )} )} diff --git a/plane-src/apps/web/core/components/inbox/content/inbox-issue-mobile-header.tsx b/plane-src/apps/web/core/components/inbox/content/inbox-issue-mobile-header.tsx index 93f9cda..0ee8b87 100644 --- a/plane-src/apps/web/core/components/inbox/content/inbox-issue-mobile-header.tsx +++ b/plane-src/apps/web/core/components/inbox/content/inbox-issue-mobile-header.tsx @@ -19,7 +19,7 @@ import { } from "@plane/propel/icons"; import type { TNameDescriptionLoader } from "@plane/types"; -import { Header, CustomMenu, EHeaderVariant } from "@plane/ui"; +import { ActionDropdown, Header, EHeaderVariant } from "@plane/ui"; import { cn, findHowManyDaysLeft, generateWorkItemLink } from "@plane/utils"; // components import { NameDescriptionUpdateStatus } from "@/components/issues/issue-update-status"; @@ -136,102 +136,112 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
- } - customButtonClassName={getIconButtonStyling("secondary", "lg")} + } + buttonClassName={getIconButtonStyling("secondary", "lg")} placement="bottom-start" - > - {isAcceptedOrDeclined && ( - -
- - {t("inbox_issue.actions.copy")} -
-
- )} - {isAcceptedOrDeclined && ( - router.push(workItemLink)}> -
- - {t("inbox_issue.actions.open")} -
-
- )} - {canMarkAsAccepted && !isAcceptedOrDeclined && ( - + items={[ + { + key: "copy", + shouldRender: !!isAcceptedOrDeclined, + action: handleCopyIssueLink, + customContent: ( +
+ + {t("inbox_issue.actions.copy")} +
+ ), + }, + { + key: "open", + shouldRender: !!isAcceptedOrDeclined, + action: () => router.push(workItemLink), + customContent: ( +
+ + {t("inbox_issue.actions.open")} +
+ ), + }, + { + key: "snooze", + shouldRender: canMarkAsAccepted && !isAcceptedOrDeclined, + action: () => handleActionWithPermission( isProjectAdmin, handleIssueSnoozeAction, t("inbox_issue.errors.snooze_permission") - ) - } - > -
- - {inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0 - ? t("inbox_issue.actions.unsnooze") - : t("inbox_issue.actions.snooze")} -
-
- )} - {canMarkAsDuplicate && !isAcceptedOrDeclined && ( - + ), + customContent: ( +
+ + {inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0 + ? t("inbox_issue.actions.unsnooze") + : t("inbox_issue.actions.snooze")} +
+ ), + }, + { + key: "duplicate", + shouldRender: canMarkAsDuplicate && !isAcceptedOrDeclined, + action: () => handleActionWithPermission( isProjectAdmin, () => setSelectDuplicateIssue(true), t("inbox_issue.errors.duplicate_permission") - ) - } - > -
- - {t("inbox_issue.actions.mark_as_duplicate")} -
-
- )} - {canMarkAsAccepted && ( - + ), + customContent: ( +
+ + {t("inbox_issue.actions.mark_as_duplicate")} +
+ ), + }, + { + key: "accept", + shouldRender: canMarkAsAccepted, + action: () => handleActionWithPermission( isProjectAdmin, () => setAcceptIssueModal(true), t("inbox_issue.errors.accept_permission") - ) - } - > -
- - {t("inbox_issue.actions.accept")} -
-
- )} - {canMarkAsDeclined && ( - + ), + customContent: ( +
+ + {t("inbox_issue.actions.accept")} +
+ ), + }, + { + key: "decline", + shouldRender: canMarkAsDeclined, + action: () => handleActionWithPermission( isProjectAdmin, () => setDeclineIssueModal(true), t("inbox_issue.errors.decline_permission") - ) - } - > -
- - {t("inbox_issue.actions.decline")} -
-
- )} - {canDelete && !isAcceptedOrDeclined && ( - setDeleteIssueModal(true)}> -
- - {t("inbox_issue.actions.delete")} -
-
- )} -
+ ), + customContent: ( +
+ + {t("inbox_issue.actions.decline")} +
+ ), + }, + { + key: "delete", + shouldRender: canDelete && !isAcceptedOrDeclined, + action: () => setDeleteIssueModal(true), + customContent: ( +
+ + {t("inbox_issue.actions.delete")} +
+ ), + }, + ]} + />
diff --git a/plane-src/apps/web/core/components/project/dropdowns/filters/member-list.tsx b/plane-src/apps/web/core/components/project/dropdowns/filters/member-list.tsx index 8996b49..a96c5ee 100644 --- a/plane-src/apps/web/core/components/project/dropdowns/filters/member-list.tsx +++ b/plane-src/apps/web/core/components/project/dropdowns/filters/member-list.tsx @@ -8,13 +8,11 @@ import { useState } from "react"; import { observer } from "mobx-react"; // plane imports import { useTranslation } from "@plane/i18n"; -import { Button } from "@plane/propel/button"; import { ChevronDownIcon } from "@plane/propel/icons"; import { EUserProjectRoles, EUserWorkspaceRoles } from "@plane/types"; // plane ui -import { CustomMenu } from "@plane/ui"; // components -import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters"; +import { FilterHeader, FilterOption, FiltersDropdown } from "@/components/issues/issue-layouts/filters"; interface IRoleOption { value: string; @@ -101,13 +99,11 @@ export const MemberListFiltersDropdown = observer(function MemberListFiltersDrop const { t } = useTranslation(); return ( - - {appliedFiltersCount > 0 && (
)} @@ -115,7 +111,9 @@ export const MemberListFiltersDropdown = observer(function MemberListFiltersDrop } placement="bottom-start" > - - +
+ +
+ ); });