From 15e81b085dceec2bf90a833d806c253130a2b011 Mon Sep 17 00:00:00 2001 From: gakshita Date: Mon, 21 Jul 2025 19:21:42 +0530 Subject: [PATCH 1/3] chore: header enchancements + quickstart guide ui update + breadcumbs truncated --- .../[workspaceSlug]/(projects)/header.tsx | 16 ++++- .../[workspaceSlug]/(projects)/layout.tsx | 1 + .../[projectId]/cycles/(detail)/header.tsx | 53 +++++++++++----- .../issues/(list)/mobile-header.tsx | 31 ++-------- .../[projectId]/modules/(detail)/header.tsx | 53 +++++++++++----- .../web/ce/components/breadcrumbs/project.tsx | 1 + apps/web/core/components/home/root.tsx | 12 +--- .../core/components/home/user-greetings.tsx | 31 ++++------ .../home/widgets/empty-states/no-projects.tsx | 22 +++---- apps/web/core/components/issues/filters.tsx | 61 ++++++++++++++----- .../filters/header/helpers/dropdown.tsx | 61 ++++++++++++------- .../issue-layouts/filters/header/index.ts | 1 + .../header/mobile-layout-selection.tsx | 54 ++++++++++++++++ .../src/breadcrumbs/navigation-dropdown.tsx | 7 ++- .../navigation-search-dropdown.tsx | 13 +++- packages/ui/src/header/helper.tsx | 2 +- 16 files changed, 278 insertions(+), 141 deletions(-) create mode 100644 apps/web/core/components/issues/issue-layouts/filters/header/mobile-layout-selection.tsx diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx index cf1be8ae27b..e398fd16672 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx @@ -3,24 +3,25 @@ import { observer } from "mobx-react"; import Image from "next/image"; import { useTheme } from "next-themes"; -import { Home } from "lucide-react"; +import { Home, Shapes } from "lucide-react"; // images import githubBlackImage from "/public/logos/github-black.png"; import githubWhiteImage from "/public/logos/github-white.png"; // ui import { GITHUB_REDIRECTED_TRACKER_EVENT, HEADER_GITHUB_ICON } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { Breadcrumbs, Header } from "@plane/ui"; +import { Breadcrumbs, Button, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common"; // constants // hooks import { captureElementAndEvent } from "@/helpers/event-tracker.helper"; +import { useHome } from "@/hooks/store/use-home"; export const WorkspaceDashboardHeader = observer(() => { // hooks const { resolvedTheme } = useTheme(); - + const { toggleWidgetSettings } = useHome(); const { t } = useTranslation(); return ( @@ -38,6 +39,15 @@ export const WorkspaceDashboardHeader = observer(() => { + captureElementAndEvent({ diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx index 2a29a72c768..35408c4914c 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx @@ -2,6 +2,7 @@ import { CommandPalette } from "@/components/command-palette"; import { AuthenticationWrapper } from "@/lib/wrappers"; +// plane web components import { WorkspaceAuthWrapper } from "@/plane-web/layouts/workspace-wrapper"; import { ProjectAppSidebar } from "./_sidebar"; 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 9a9df86471f..bf3948859e4 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 @@ -4,7 +4,7 @@ import { useCallback, useRef, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // icons -import { PanelRight } from "lucide-react"; +import { ChartNoAxesColumn, ListFilter, PanelRight, SlidersHorizontal } from "lucide-react"; // plane imports import { EIssueFilterType, @@ -30,7 +30,13 @@ import { cn, isIssueFilterActive } from "@plane/utils"; import { WorkItemsModal } from "@/components/analytics/work-items/modal"; import { SwitcherLabel } from "@/components/common"; import { CycleQuickActions } from "@/components/cycles"; -import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues"; +import { + DisplayFiltersSelection, + FiltersDropdown, + FilterSelection, + LayoutSelection, + MobileLayoutSelection, +} from "@/components/issues"; // hooks import { useCommandPalette, @@ -207,21 +213,31 @@ export const CycleIssuesHeader: React.FC = observer(() => {
- handleLayoutChange(layout)} - selectedLayout={activeLayout} - /> +
+ handleLayoutChange(layout)} + selectedLayout={activeLayout} + /> +
+
+ handleLayoutChange(layout)} + activeLayout={activeLayout} + /> +
} > { moduleViewDisabled={!currentProjectDetails?.module_view} /> - + } + > { {canUserCreateIssue && ( <> {!isCompletedCycle && ( +
+

+ {t("good")} {t(greeting)}, {user?.first_name} {user?.last_name} +

+
+
{greeting === "morning" ? "🌤️" : greeting === "afternoon" ? "🌥️" : "🌙️"}
+
+ {weekDay}, {date} {timeString} +
+
); }; diff --git a/apps/web/core/components/home/widgets/empty-states/no-projects.tsx b/apps/web/core/components/home/widgets/empty-states/no-projects.tsx index 8621029eaa4..7dd608b4078 100644 --- a/apps/web/core/components/home/widgets/empty-states/no-projects.tsx +++ b/apps/web/core/components/home/widgets/empty-states/no-projects.tsx @@ -44,7 +44,7 @@ export const NoProjectsEmptyState = observer(() => { id: "create-project", title: "home.empty.create_project.title", description: "home.empty.create_project.description", - icon: , + icon: , flag: "projects", cta: { text: "home.empty.create_project.cta", @@ -62,7 +62,7 @@ export const NoProjectsEmptyState = observer(() => { id: "invite-team", title: "home.empty.invite_team.title", description: "home.empty.invite_team.description", - icon: , + icon: , flag: "visited_members", cta: { text: "home.empty.invite_team.cta", @@ -74,7 +74,7 @@ export const NoProjectsEmptyState = observer(() => { id: "configure-workspace", title: "home.empty.configure_workspace.title", description: "home.empty.configure_workspace.description", - icon: , + icon: , flag: "visited_workspace", cta: { text: "home.empty.configure_workspace.cta", @@ -89,7 +89,7 @@ export const NoProjectsEmptyState = observer(() => { icon: currentUser?.avatar_url && currentUser?.avatar_url.trim() !== "" ? ( - + { ) : ( - + {(currentUser?.email ?? currentUser?.display_name ?? "?")[0]} @@ -142,17 +142,17 @@ export const NoProjectsEmptyState = observer(() => { {t("home.empty.not_right_now")}
-
+
{EMPTY_STATE_DATA.map((item) => { const isStateComplete = isComplete(item.flag); return (
{ > {item.icon}
-

{t(item.title)}

-

{t(item.description)}

+

{t(item.title)}

+

{t(item.description)}

{isStateComplete ? ( -
+
) : ( diff --git a/apps/web/core/components/issues/filters.tsx b/apps/web/core/components/issues/filters.tsx index 25d2ba6ee4e..12d99143a1e 100644 --- a/apps/web/core/components/issues/filters.tsx +++ b/apps/web/core/components/issues/filters.tsx @@ -17,13 +17,21 @@ import { import { Button } from "@plane/ui"; // components import { isIssueFilterActive } from "@plane/utils"; -import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues"; +import { + DisplayFiltersSelection, + FiltersDropdown, + FilterSelection, + IssueLayoutIcon, + LayoutSelection, + MobileLayoutSelection, +} from "@/components/issues"; // helpers // hooks import { useLabel, useProjectState, useMember, useIssues } from "@/hooks/store"; // plane web types import { TProject } from "@/plane-web/types"; import { WorkItemsModal } from "../analytics/work-items/modal"; +import { ChartNoAxesColumn, ChevronDown, ListFilter, SlidersHorizontal } from "lucide-react"; type Props = { currentProjectDetails: TProject | undefined; @@ -32,6 +40,13 @@ type Props = { canUserCreateIssue: boolean | undefined; storeType?: EIssuesStoreType.PROJECT | EIssuesStoreType.EPIC; }; +const LAYOUTS = [ + EIssueLayoutTypes.LIST, + EIssueLayoutTypes.KANBAN, + EIssueLayoutTypes.CALENDAR, + EIssueLayoutTypes.SPREADSHEET, + EIssueLayoutTypes.GANTT, +]; const HeaderFilters = observer((props: Props) => { const { currentProjectDetails, @@ -109,21 +124,25 @@ const HeaderFilters = observer((props: Props) => { projectDetails={currentProjectDetails ?? undefined} isEpic={storeType === EIssuesStoreType.EPIC} /> - handleLayoutChange(layout)} - selectedLayout={activeLayout} - /> +
+ handleLayoutChange(layout)} + selectedLayout={activeLayout} + /> +
+
+ handleLayoutChange(layout)} + activeLayout={activeLayout} + /> +
} > { isEpic={storeType === EIssuesStoreType.EPIC} /> - + } + title={t("common.display")} + placement="bottom-end" + > { /> {canUserCreateIssue ? ( - ) : ( <> diff --git a/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx b/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx index f7c8deab1c7..c831b647ed5 100644 --- a/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx +++ b/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx @@ -13,6 +13,7 @@ import { Button } from "@plane/ui"; type Props = { children: React.ReactNode; icon?: React.ReactNode; + miniIcon?: React.ReactNode; title?: string; placement?: Placement; disabled?: boolean; @@ -24,6 +25,7 @@ type Props = { export const FiltersDropdown: React.FC = (props) => { const { children, + miniIcon, icon, title = "Dropdown", placement, @@ -33,7 +35,7 @@ export const FiltersDropdown: React.FC = (props) => { isFiltersApplied = false, } = props; - const [referenceElement, setReferenceElement] = useState(null); + const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const { styles, attributes } = usePopper(referenceElement, popperElement, { @@ -53,27 +55,42 @@ export const FiltersDropdown: React.FC = (props) => { {menuButton} ) : ( - +
+
+ +
+
+ +
+
)} void; + activeLayout?: EIssueLayoutTypes; + isMobile?: boolean; +}) => { + const { t } = useTranslation(); + return ( + + + + + ) : ( +
+ {t("common.layout")} + +
+ ) + } + customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm" + closeOnSelect + > + {ISSUE_LAYOUTS.filter((l) => layouts.includes(l.key)).map((layout, index) => ( + { + onChange(layout.key); + }} + className="flex items-center gap-2" + > + +
{t(layout.i18n_title)}
+
+ ))} +
+ ); +}; diff --git a/packages/ui/src/breadcrumbs/navigation-dropdown.tsx b/packages/ui/src/breadcrumbs/navigation-dropdown.tsx index 503e13eb2fe..c07513e4aae 100644 --- a/packages/ui/src/breadcrumbs/navigation-dropdown.tsx +++ b/packages/ui/src/breadcrumbs/navigation-dropdown.tsx @@ -45,8 +45,11 @@ export const BreadcrumbNavigationDropdown = (props: TBreadcrumbNavigationDropdow } )} > - {selectedItemIcon && {selectedItemIcon}} - {selectedItem.title} +
...
+
+ {selectedItemIcon && {selectedItemIcon}} + {selectedItem.title} +
); diff --git a/packages/ui/src/breadcrumbs/navigation-search-dropdown.tsx b/packages/ui/src/breadcrumbs/navigation-search-dropdown.tsx index 0439d1d3333..75cfff35a77 100644 --- a/packages/ui/src/breadcrumbs/navigation-search-dropdown.tsx +++ b/packages/ui/src/breadcrumbs/navigation-search-dropdown.tsx @@ -16,6 +16,7 @@ type TBreadcrumbNavigationSearchDropdownProps = { isLast?: boolean; handleOnClick?: () => void; disableRootHover?: boolean; + shouldTruncate?: boolean; }; export const BreadcrumbNavigationSearchDropdown: React.FC = (props) => { @@ -28,6 +29,7 @@ export const BreadcrumbNavigationSearchDropdown: React.FC - {icon && {icon}} - {title} + {shouldTruncate &&
...
} +
+ {icon && {icon}} + {title} +
{ const height = setMinHeight ? minHeights[variant] : ""; const display = variant === EHeaderVariant.SECONDARY ? (showOnMobile ? "flex" : "hidden md:flex") : ""; - return " " + headerStyle[variant] + " " + height + " " + display; + return " @container " + headerStyle[variant] + " " + height + " " + display; }; From fff1dd8344c7bf7e62a6de9034ae0bc3f20ecbfd Mon Sep 17 00:00:00 2001 From: gakshita Date: Mon, 21 Jul 2025 19:32:00 +0530 Subject: [PATCH 2/3] fix: css scrollbar --- packages/tailwind-config/tailwind.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/tailwind-config/tailwind.config.js b/packages/tailwind-config/tailwind.config.js index 8226013aa91..42e98e0a5b3 100644 --- a/packages/tailwind-config/tailwind.config.js +++ b/packages/tailwind-config/tailwind.config.js @@ -487,6 +487,14 @@ module.exports = { paddingRight: "1.35rem", }, }, + // Hide scrollbar but keep functionality + ".scrollbar-hide": { + "-ms-overflow-style": "none" /* IE and Edge */, + "scrollbar-width": "none" /* Firefox */, + "&::-webkit-scrollbar": { + display: "none" /* Chrome, Safari and Opera */, + }, + }, }; addUtilities(newUtilities, ["responsive"]); From 77ddb33b5921ffedaf8c24400529d3519c47679c Mon Sep 17 00:00:00 2001 From: gakshita Date: Tue, 22 Jul 2025 15:31:05 +0530 Subject: [PATCH 3/3] chore: seeperated header --- .../ce/components/common/extended-app-header.tsx | 16 ++++++++++++++++ apps/web/ce/components/common/index.ts | 1 + apps/web/core/components/core/app-header.tsx | 9 ++------- .../ee/components/common/extended-app-header.tsx | 1 + apps/web/ee/components/common/index.ts | 1 + 5 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 apps/web/ce/components/common/extended-app-header.tsx create mode 100644 apps/web/ee/components/common/extended-app-header.tsx create mode 100644 apps/web/ee/components/common/index.ts diff --git a/apps/web/ce/components/common/extended-app-header.tsx b/apps/web/ce/components/common/extended-app-header.tsx new file mode 100644 index 00000000000..a8f83b339c1 --- /dev/null +++ b/apps/web/ce/components/common/extended-app-header.tsx @@ -0,0 +1,16 @@ +import { ReactNode } from "react"; +import { AppSidebarToggleButton } from "@/components/sidebar"; +import { useAppTheme } from "@/hooks/store/use-app-theme"; + +export const ExtendedAppHeader = (props: { header: ReactNode }) => { + const { header } = props; + // store hooks + const { sidebarCollapsed } = useAppTheme(); + + return ( + <> + {sidebarCollapsed && } +
{header}
+ + ); +}; diff --git a/apps/web/ce/components/common/index.ts b/apps/web/ce/components/common/index.ts index 75c2cf410a8..38406d4cc32 100644 --- a/apps/web/ce/components/common/index.ts +++ b/apps/web/ce/components/common/index.ts @@ -1 +1,2 @@ export * from "./subscription"; +export * from "./extended-app-header"; diff --git a/apps/web/core/components/core/app-header.tsx b/apps/web/core/components/core/app-header.tsx index c911d9096e5..a63d5ab9374 100644 --- a/apps/web/core/components/core/app-header.tsx +++ b/apps/web/core/components/core/app-header.tsx @@ -5,9 +5,7 @@ import { observer } from "mobx-react"; // plane imports import { Row } from "@plane/ui"; // components -import { AppSidebarToggleButton } from "@/components/sidebar"; -// hooks -import { useAppTheme } from "@/hooks/store"; +import { ExtendedAppHeader } from "@/plane-web/components/common"; export interface AppHeaderProps { header: ReactNode; @@ -16,14 +14,11 @@ export interface AppHeaderProps { export const AppHeader = observer((props: AppHeaderProps) => { const { header, mobileHeader } = props; - // store hooks - const { sidebarCollapsed } = useAppTheme(); return (
- {sidebarCollapsed && } -
{header}
+
{mobileHeader && mobileHeader}
diff --git a/apps/web/ee/components/common/extended-app-header.tsx b/apps/web/ee/components/common/extended-app-header.tsx new file mode 100644 index 00000000000..3719b5662b2 --- /dev/null +++ b/apps/web/ee/components/common/extended-app-header.tsx @@ -0,0 +1 @@ +export * from "./extended-app-header"; diff --git a/apps/web/ee/components/common/index.ts b/apps/web/ee/components/common/index.ts new file mode 100644 index 00000000000..3719b5662b2 --- /dev/null +++ b/apps/web/ee/components/common/index.ts @@ -0,0 +1 @@ +export * from "./extended-app-header";