diff --git a/web/app/[workspaceSlug]/(projects)/notifications/page.tsx b/web/app/[workspaceSlug]/(projects)/notifications/page.tsx
index 5fa81a8e1..c6003c259 100644
--- a/web/app/[workspaceSlug]/(projects)/notifications/page.tsx
+++ b/web/app/[workspaceSlug]/(projects)/notifications/page.tsx
@@ -6,9 +6,11 @@ import useSWR from "swr";
// components
import { LogoSpinner } from "@/components/common";
import { PageHead } from "@/components/core";
+import { EmptyState } from "@/components/empty-state";
import { InboxContentRoot } from "@/components/inbox";
import { IssuePeekOverview } from "@/components/issues";
// constants
+import { EmptyStateType } from "@/constants/empty-state";
import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification";
// hooks
import { useIssueDetail, useUser, useWorkspace, useWorkspaceNotifications } from "@/hooks/store";
@@ -62,36 +64,44 @@ const WorkspaceDashboardPage = observer(() => {
setCurrentSelectedNotificationId(undefined);
setPeekIssue(undefined);
},
- []
+ [setCurrentSelectedNotificationId, setPeekIssue]
);
return (
<>
- {is_inbox_issue === true && workspace_slug && project_id && issue_id ? (
+ {!currentSelectedNotificationId ? (
+
+
+
+ ) : (
<>
- {projectMemberInfoLoader ? (
-
-
-
+ {is_inbox_issue === true && workspace_slug && project_id && issue_id ? (
+ <>
+ {projectMemberInfoLoader ? (
+
+
+
+ ) : (
+
{}}
+ isMobileSidebar={false}
+ workspaceSlug={workspace_slug}
+ projectId={project_id}
+ inboxIssueId={issue_id}
+ isNotificationEmbed
+ embedRemoveCurrentNotification={() => setCurrentSelectedNotificationId(undefined)}
+ />
+ )}
+ >
) : (
- {}}
- isMobileSidebar={false}
- workspaceSlug={workspace_slug}
- projectId={project_id}
- inboxIssueId={issue_id}
- isNotificationEmbed
+ setCurrentSelectedNotificationId(undefined)}
/>
)}
>
- ) : (
- setCurrentSelectedNotificationId(undefined)}
- />
)}
>
diff --git a/web/core/components/workspace-notifications/notification-app-sidebar-option.tsx b/web/core/components/workspace-notifications/notification-app-sidebar-option.tsx
index 3d33bdd72..cfffe1265 100644
--- a/web/core/components/workspace-notifications/notification-app-sidebar-option.tsx
+++ b/web/core/components/workspace-notifications/notification-app-sidebar-option.tsx
@@ -38,7 +38,7 @@ export const NotificationAppSidebarOption: FC = o
return (
-
+
);
});
diff --git a/web/core/components/workspace-notifications/sidebar/filters/index.ts b/web/core/components/workspace-notifications/sidebar/filters/index.ts
index 4f7dbf49f..b4d269c8a 100644
--- a/web/core/components/workspace-notifications/sidebar/filters/index.ts
+++ b/web/core/components/workspace-notifications/sidebar/filters/index.ts
@@ -1,2 +1,2 @@
-export * from "./root";
+export * from "./menu";
export * from "./applied-filter";
diff --git a/web/core/components/workspace-notifications/sidebar/filters/menu/index.ts b/web/core/components/workspace-notifications/sidebar/filters/menu/index.ts
new file mode 100644
index 000000000..5d76f4263
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/filters/menu/index.ts
@@ -0,0 +1,2 @@
+export * from "./root";
+export * from "./menu-option-item";
diff --git a/web/core/components/workspace-notifications/sidebar/filters/menu/menu-option-item.tsx b/web/core/components/workspace-notifications/sidebar/filters/menu/menu-option-item.tsx
new file mode 100644
index 000000000..cf894e187
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/filters/menu/menu-option-item.tsx
@@ -0,0 +1,46 @@
+"use client";
+
+import { FC } from "react";
+import { observer } from "mobx-react";
+import { Check } from "lucide-react";
+// constants
+import { ENotificationFilterType } from "@/constants/notification";
+// helpers
+import { cn } from "@/helpers/common.helper";
+// hooks
+import { useWorkspaceNotifications } from "@/hooks/store";
+
+export const NotificationFilterOptionItem: FC<{ label: string; value: ENotificationFilterType }> = observer((props) => {
+ const { value, label } = props;
+ // hooks
+ const { filters, updateFilters } = useWorkspaceNotifications();
+
+ const handleFilterTypeChange = (filterType: ENotificationFilterType, filterValue: boolean) =>
+ updateFilters("type", {
+ ...filters.type,
+ [filterType]: filterValue,
+ });
+
+ // derived values
+ const isSelected = filters?.type?.[value] || false;
+
+ return (
+ handleFilterTypeChange(value, !isSelected)}
+ >
+
+ {isSelected && }
+
+
+ {label}
+
+
+ );
+});
diff --git a/web/core/components/workspace-notifications/sidebar/filters/menu/root.tsx b/web/core/components/workspace-notifications/sidebar/filters/menu/root.tsx
new file mode 100644
index 000000000..09f8427d3
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/filters/menu/root.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import { FC } from "react";
+import { observer } from "mobx-react";
+import { ListFilter } from "lucide-react";
+import { PopoverMenu, Tooltip } from "@plane/ui";
+// components
+import { NotificationFilterOptionItem } from "@/components/workspace-notifications";
+// constants
+import { ENotificationFilterType, FILTER_TYPE_OPTIONS } from "@/constants/notification";
+// hooks
+import { usePlatformOS } from "@/hooks/use-platform-os";
+
+export const NotificationFilter: FC = observer(() => {
+ // hooks
+ const { isMobile } = usePlatformOS();
+
+ return (
+
+
+
+
+
+ }
+ keyExtractor={(item: { label: string; value: ENotificationFilterType }) => item.value}
+ render={(item) => }
+ />
+ );
+});
diff --git a/web/core/components/workspace-notifications/sidebar/filters/root.tsx b/web/core/components/workspace-notifications/sidebar/filters/root.tsx
deleted file mode 100644
index 86994c819..000000000
--- a/web/core/components/workspace-notifications/sidebar/filters/root.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-"use client";
-
-import { FC, Fragment } from "react";
-import { observer } from "mobx-react";
-import { Check, ListFilter } from "lucide-react";
-import { Popover, Transition } from "@headlessui/react";
-import { Tooltip } from "@plane/ui";
-// constants
-import { ENotificationFilterType, FILTER_TYPE_OPTIONS } from "@/constants/notification";
-// helpers
-import { cn } from "@/helpers/common.helper";
-// hooks
-import { useWorkspaceNotifications } from "@/hooks/store";
-import { usePlatformOS } from "@/hooks/use-platform-os";
-
-export const NotificationFilter: FC = observer(() => {
- // hooks
- const { isMobile } = usePlatformOS();
- const { filters, updateFilters } = useWorkspaceNotifications();
-
- const handleFilterTypeChange = (filterType: ENotificationFilterType, filterValue: boolean) =>
- updateFilters("type", {
- ...filters.type,
- [filterType]: filterValue,
- });
-
- return (
-
-
- (open ? "bg-custom-background-80" : "")
- )}
- >
-
-
-
-
-
-
-
- {FILTER_TYPE_OPTIONS.map((filter) => {
- const isSelected = filters?.type?.[filter?.value] || false;
- return (
-
handleFilterTypeChange(filter?.value, !isSelected)}
- >
-
- {isSelected && }
-
-
- {filter.label}
-
-
- );
- })}
-
-
-
-
- );
-});
diff --git a/web/core/components/workspace-notifications/sidebar/header/options/menu-option.tsx b/web/core/components/workspace-notifications/sidebar/header/options/menu-option.tsx
deleted file mode 100644
index 509dd9de3..000000000
--- a/web/core/components/workspace-notifications/sidebar/header/options/menu-option.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-"use client";
-
-import { FC, Fragment } from "react";
-import { observer } from "mobx-react";
-import { Check, CheckCheck, CheckCircle, Clock, MoreVertical } from "lucide-react";
-import { Popover, Transition } from "@headlessui/react";
-import { TNotificationFilter } from "@plane/types";
-import { ArchiveIcon, Spinner, Tooltip } from "@plane/ui";
-// constants
-import { NOTIFICATIONS_READ } from "@/constants/event-tracker";
-import { ENotificationLoader } from "@/constants/notification";
-import { cn } from "@/helpers/common.helper";
-// hooks
-import { useEventTracker, useWorkspaceNotifications } from "@/hooks/store";
-import { usePlatformOS } from "@/hooks/use-platform-os";
-
-type TNotificationHeaderMenuOption = {
- workspaceSlug: string;
-};
-
-export const NotificationHeaderMenuOption: FC = observer((props) => {
- const { workspaceSlug } = props;
- // hooks
- const { captureEvent } = useEventTracker();
- const { isMobile } = usePlatformOS();
- const { loader, filters, updateFilters, updateBulkFilters, markAllNotificationsAsRead } = useWorkspaceNotifications();
-
- const handleFilterChange = (filterType: keyof TNotificationFilter, filterValue: boolean) =>
- updateFilters(filterType, filterValue);
-
- const handleBulkFilterChange = (filter: Partial) => updateBulkFilters(filter);
-
- const handleMarkAllNotificationsAsRead = async () => {
- // NOTE: We are using loader to prevent continues request when we are making all the notification to read
- if (loader) return;
- try {
- await markAllNotificationsAsRead(workspaceSlug);
- } catch (error) {
- console.error(error);
- }
- };
-
- return (
-
-
- (open ? "bg-custom-background-80" : "")
- )}
- >
-
-
-
-
-
-
-
-
-
{
- handleMarkAllNotificationsAsRead();
- captureEvent(NOTIFICATIONS_READ);
- }}
- >
-
-
Mark all as read
- {loader === ENotificationLoader.MARK_ALL_AS_READY && (
-
-
-
- )}
-
-
-
-
-
-
-
handleFilterChange("read", !filters?.read)}
- >
-
-
- Show unread
-
- {filters?.read && (
-
-
-
- )}
-
-
-
- handleBulkFilterChange({
- archived: !filters?.archived,
- snoozed: false,
- })
- }
- >
-
-
- Show Archived
-
- {filters?.archived && (
-
-
-
- )}
-
-
-
- handleBulkFilterChange({
- snoozed: !filters?.snoozed,
- archived: false,
- })
- }
- >
-
-
- Show Snoozed
-
- {filters?.snoozed && (
-
-
-
- )}
-
-
-
-
-
-
- );
-});
diff --git a/web/core/components/workspace-notifications/sidebar/header/options/menu-option/index.ts b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/index.ts
new file mode 100644
index 000000000..808d90c76
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/index.ts
@@ -0,0 +1,2 @@
+export * from "./root";
+export * from "./menu-item";
diff --git a/web/core/components/workspace-notifications/sidebar/header/options/menu-option/menu-item.tsx b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/menu-item.tsx
new file mode 100644
index 000000000..918966310
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/menu-item.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { FC } from "react";
+import { observer } from "mobx-react";
+// components
+import type { TPopoverMenuOptions } from "@/components/workspace-notifications";
+// helpers
+import { cn } from "@/helpers/common.helper";
+
+export const NotificationMenuOptionItem: FC = observer((props) => {
+ const { type, label = "", isActive, prependIcon, appendIcon, onClick } = props;
+
+ if (type === "menu-item")
+ return (
+ onClick && onClick()}
+ >
+ {prependIcon && prependIcon}
+
+ {label}
+
+ {appendIcon &&
{appendIcon}
}
+
+ );
+
+ return ;
+});
diff --git a/web/core/components/workspace-notifications/sidebar/header/options/menu-option/root.tsx b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/root.tsx
new file mode 100644
index 000000000..ea0eb130f
--- /dev/null
+++ b/web/core/components/workspace-notifications/sidebar/header/options/menu-option/root.tsx
@@ -0,0 +1,114 @@
+"use client";
+
+import { FC, ReactNode } from "react";
+import { observer } from "mobx-react";
+import { Check, CheckCheck, CheckCircle, Clock } from "lucide-react";
+import { TNotificationFilter } from "@plane/types";
+import { ArchiveIcon, PopoverMenu, Spinner } from "@plane/ui";
+// components
+import { NotificationMenuOptionItem } from "@/components/workspace-notifications";
+// constants
+import { NOTIFICATIONS_READ } from "@/constants/event-tracker";
+import { ENotificationLoader } from "@/constants/notification";
+// hooks
+import { useEventTracker, useWorkspaceNotifications } from "@/hooks/store";
+
+type TNotificationHeaderMenuOption = {
+ workspaceSlug: string;
+};
+
+export type TPopoverMenuOptions = {
+ key: string;
+ type: string;
+ label?: string | undefined;
+ isActive?: boolean | undefined;
+ prependIcon?: ReactNode | undefined;
+ appendIcon?: ReactNode | undefined;
+ onClick?: (() => void) | undefined;
+};
+
+export const NotificationHeaderMenuOption: FC = observer((props) => {
+ const { workspaceSlug } = props;
+ // hooks
+ const { captureEvent } = useEventTracker();
+ const { loader, filters, updateFilters, updateBulkFilters, markAllNotificationsAsRead } = useWorkspaceNotifications();
+
+ const handleFilterChange = (filterType: keyof TNotificationFilter, filterValue: boolean) =>
+ updateFilters(filterType, filterValue);
+
+ const handleBulkFilterChange = (filter: Partial) => updateBulkFilters(filter);
+
+ const handleMarkAllNotificationsAsRead = async () => {
+ // NOTE: We are using loader to prevent continues request when we are making all the notification to read
+ if (loader) return;
+ try {
+ await markAllNotificationsAsRead(workspaceSlug);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const popoverMenuOptions: TPopoverMenuOptions[] = [
+ {
+ key: "menu-mark-all-read",
+ type: "menu-item",
+ label: "Mark all as read",
+ isActive: true,
+ prependIcon: ,
+ appendIcon: loader === ENotificationLoader.MARK_ALL_AS_READY ? : undefined,
+ onClick: () => {
+ captureEvent(NOTIFICATIONS_READ);
+ handleMarkAllNotificationsAsRead();
+ },
+ },
+ {
+ key: "menu-divider",
+ type: "divider",
+ },
+ {
+ key: "menu-unread",
+ type: "menu-item",
+ label: "Show unread",
+ isActive: filters?.read,
+ prependIcon: ,
+ appendIcon: filters?.read ? : undefined,
+ onClick: () => handleFilterChange("read", !filters?.read),
+ },
+ {
+ key: "menu-archived",
+ type: "menu-item",
+ label: "Show archived",
+ isActive: filters?.archived,
+ prependIcon: ,
+ appendIcon: filters?.archived ? : undefined,
+ onClick: () =>
+ handleBulkFilterChange({
+ archived: !filters?.archived,
+ snoozed: false,
+ }),
+ },
+ {
+ key: "menu-snoozed",
+ type: "menu-item",
+ label: "Show snoozed",
+ isActive: filters?.snoozed,
+ prependIcon: ,
+ appendIcon: filters?.snoozed ? : undefined,
+ onClick: () =>
+ handleBulkFilterChange({
+ snoozed: !filters?.snoozed,
+ archived: false,
+ }),
+ },
+ ];
+
+ return (
+ item.key}
+ panelClassName="p-0 py-2 rounded-md border border-custom-border-200 bg-custom-background-100 space-y-1"
+ render={(item: TPopoverMenuOptions) => }
+ />
+ );
+});
diff --git a/web/core/components/workspace-notifications/sidebar/root.tsx b/web/core/components/workspace-notifications/sidebar/root.tsx
index 679df0b31..26ad1d76a 100644
--- a/web/core/components/workspace-notifications/sidebar/root.tsx
+++ b/web/core/components/workspace-notifications/sidebar/root.tsx
@@ -66,12 +66,9 @@ export const NotificationsSidebar: FC = observer(() => {
)}
>
{tab.label}
-
+ {tab.count(unreadNotificationsCount) > 0 && (
+
+ )}
{currentNotificationTab === tab.value && (
diff --git a/web/core/constants/empty-state.ts b/web/core/constants/empty-state.ts
index cad133dd0..56eb1d472 100644
--- a/web/core/constants/empty-state.ts
+++ b/web/core/constants/empty-state.ts
@@ -80,6 +80,7 @@ export enum EmptyStateType {
ISSUE_RELATION_EMPTY_STATE = "issue-relation-empty-state",
ISSUE_COMMENT_EMPTY_STATE = "issue-comment-empty-state",
+ NOTIFICATION_DETAIL_EMPTY_STATE = "notification-detail-empty-state",
NOTIFICATION_ALL_EMPTY_STATE = "notification-all-empty-state",
NOTIFICATION_MENTIONS_EMPTY_STATE = "notification-mentions-empty-state",
NOTIFICATION_MY_ISSUE_EMPTY_STATE = "notification-my-issues-empty-state",
@@ -595,6 +596,11 @@ const emptyStateDetails = {
path: "/empty-state/search/comments",
},
+ [EmptyStateType.NOTIFICATION_DETAIL_EMPTY_STATE]: {
+ key: EmptyStateType.INBOX_DETAIL_EMPTY_STATE,
+ title: "Select to view details.",
+ path: "/empty-state/inbox/issue-detail",
+ },
[EmptyStateType.NOTIFICATION_ALL_EMPTY_STATE]: {
key: EmptyStateType.NOTIFICATION_ALL_EMPTY_STATE,
title: "No issues assigned",