From fe5ce937bddf6c7759d3a689d2062be892014ca0 Mon Sep 17 00:00:00 2001 From: vamsi krishna Date: Tue, 3 Dec 2024 12:43:50 +0530 Subject: [PATCH] added pop over --- .../(projects)/notifications/layout.tsx | 4 +- .../notification-card/index.ts | 5 +- .../notification-card/item.tsx | 101 ++++++++++++++---- .../notification-card/option/root.tsx | 57 ++++++++++ .../notification-card/preview/index.ts | 1 + .../notification-card/preview/root.tsx | 30 ++++++ .../notification-card/root.tsx | 8 +- .../workspace-notifications/root.tsx | 6 +- .../notifications.store.ts | 32 +++--- 9 files changed, 200 insertions(+), 44 deletions(-) create mode 100644 web/ee/components/workspace-notifications/notification-card/option/root.tsx create mode 100644 web/ee/components/workspace-notifications/notification-card/preview/index.ts create mode 100644 web/ee/components/workspace-notifications/notification-card/preview/root.tsx diff --git a/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx b/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx index e3d7303636..f7ee7e0788 100644 --- a/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx +++ b/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx @@ -5,8 +5,8 @@ import { NotificationsSidebarRoot } from "@/components/workspace-notifications"; export default function ProjectInboxIssuesLayout({ children }: { children: React.ReactNode }) { return ( -
- +
+
{children}
); diff --git a/web/ee/components/workspace-notifications/notification-card/index.ts b/web/ee/components/workspace-notifications/notification-card/index.ts index 9936e7c4b4..cf944bcb6f 100644 --- a/web/ee/components/workspace-notifications/notification-card/index.ts +++ b/web/ee/components/workspace-notifications/notification-card/index.ts @@ -1,2 +1,3 @@ -export * from './root'; -export * from './item' \ No newline at end of file +export * from "./root"; +export * from "./item"; +export * from "./preview"; diff --git a/web/ee/components/workspace-notifications/notification-card/item.tsx b/web/ee/components/workspace-notifications/notification-card/item.tsx index 2d0ed680b9..ef8a1f7717 100644 --- a/web/ee/components/workspace-notifications/notification-card/item.tsx +++ b/web/ee/components/workspace-notifications/notification-card/item.tsx @@ -1,25 +1,90 @@ -import { FC } from 'react' +import { FC, useMemo, useState } from "react"; +import orderBy from "lodash/orderBy"; import { observer } from "mobx-react"; -import { Row } from '@plane/ui'; +import { usePopper } from "react-popper"; +import { Popover } from "@headlessui/react"; +import { Row } from "@plane/ui"; +import { MemberDropdown } from "@/components/dropdowns"; +import { calculateTimeAgo, convertToEpoch } from "@/helpers/date-time.helper"; //store -import { useIssueDetail } from "@/hooks/store" +import { useWorkspaceNotifications } from "@/hooks/store"; //components -import { IssueIdentifier } from '@/plane-web/components/issues'; +import { NotificationCardPreview } from "@/plane-web/components/workspace-notifications"; export interface INotificationItem { - issueId: string; - notificationsCount: number; + issueId: string; } -export const NotificationItem: FC = observer((props)=>{ - const { issueId, notificationsCount } = props; - const { issue: { getIssueById }} = useIssueDetail(); - const issueDetail = getIssueById(issueId); +export const NotificationItem: FC = observer((props) => { + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); - if(!issueDetail) return null - return ( -
- - - + const { issueId } = props; + const { groupedNotifications } = useWorkspaceNotifications(); + const notificationGroup = groupedNotifications[issueId]; + const issue = notificationGroup[0].data?.issue; + const unreadCount = notificationGroup.filter((e) => !e.read_at).length; + const authorIds = notificationGroup + .map((e) => e.triggered_by) + .filter((id) => id != undefined && id != null) as string[]; + + if (!notificationGroup || !issue || !authorIds) return <>; + + const latestNotificationTime = useMemo(() => { + const latestNotification = orderBy(notificationGroup, (n) => convertToEpoch(n.created_at), "desc")[0]; + if (latestNotification.created_at) return calculateTimeAgo(latestNotification.created_at); + }, [notificationGroup]); + + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: "right-end", + }); + + return ( + + +
+ {/* Issue card header */} + + + {issue.sequence_id}-{issue.identifier} + +
+ + {issue.name} + + {unreadCount > 0 && ( + + {unreadCount} + + )} +
+
+ + {/* Assignesss avatars / author avatars */} + {}} + disabled + multiple + buttonVariant={authorIds?.length > 0 ? "transparent-without-text" : "border-without-text"} + buttonClassName={authorIds?.length > 0 ? "hover:bg-transparent px-0" : ""} + showTooltip={authorIds?.length === 0} + placeholder="Assignees" + optionsClassName="z-10" + tooltipContent="" + /> +
+ {latestNotificationTime} +
- ) -}) \ No newline at end of file + + +
+ +
+
+ + ); +}); diff --git a/web/ee/components/workspace-notifications/notification-card/option/root.tsx b/web/ee/components/workspace-notifications/notification-card/option/root.tsx new file mode 100644 index 0000000000..2017c0db8d --- /dev/null +++ b/web/ee/components/workspace-notifications/notification-card/option/root.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { FC, Dispatch, SetStateAction } from "react"; +import { observer } from "mobx-react"; +// components +import { + NotificationItemReadOption, + NotificationItemArchiveOption, + NotificationItemSnoozeOption, +} from "@/components/workspace-notifications"; +// helpers +import { cn } from "@/helpers/common.helper"; +// hooks +import { useNotification } from "@/hooks/store"; + +type TNotificationOption = { + workspaceSlug: string; + notificationId: string; + isSnoozeStateModalOpen: boolean; + setIsSnoozeStateModalOpen: Dispatch>; + customSnoozeModal: boolean; + setCustomSnoozeModal: Dispatch>; +}; + +export const NotificationOption: FC = observer((props) => { + const { + workspaceSlug, + notificationId, + isSnoozeStateModalOpen, + setIsSnoozeStateModalOpen, + customSnoozeModal, + setCustomSnoozeModal, + } = props; + // hooks + const notification = useNotification(notificationId); + + return ( + + ); +}); diff --git a/web/ee/components/workspace-notifications/notification-card/preview/index.ts b/web/ee/components/workspace-notifications/notification-card/preview/index.ts new file mode 100644 index 0000000000..1efe34c51e --- /dev/null +++ b/web/ee/components/workspace-notifications/notification-card/preview/index.ts @@ -0,0 +1 @@ +export * from "./root"; diff --git a/web/ee/components/workspace-notifications/notification-card/preview/root.tsx b/web/ee/components/workspace-notifications/notification-card/preview/root.tsx new file mode 100644 index 0000000000..a254a99651 --- /dev/null +++ b/web/ee/components/workspace-notifications/notification-card/preview/root.tsx @@ -0,0 +1,30 @@ +import { FC } from "react"; +import { TNotification } from "@plane/types"; +import { Row } from "@plane/ui"; + +export type TNotificationCardPreview = { + notificationGroup: TNotification[]; +}; +export const NotificationCardPreview: FC = (props) => { + const { notificationGroup } = props; + const issue = notificationGroup[0].data?.issue; + const unreadCount = notificationGroup.filter((e) => !e.read_at).length; + + if (!issue) return; + return ( +
+
+

+ {issue.identifier}-{issue.sequence_id} +

+ {unreadCount > 0 && ( + + {unreadCount} new updates + + )} +
+

{issue.name}

+
{/* Lastest notifications */}
+
+ ); +}; diff --git a/web/ee/components/workspace-notifications/notification-card/root.tsx b/web/ee/components/workspace-notifications/notification-card/root.tsx index ab8196d877..7db6d5df42 100644 --- a/web/ee/components/workspace-notifications/notification-card/root.tsx +++ b/web/ee/components/workspace-notifications/notification-card/root.tsx @@ -17,7 +17,7 @@ type TNotificationCardListRoot = { export const NotificationCardListRoot: FC = observer((props) => { const { workspaceSlug, workspaceId } = props; // hooks - const { loader, paginationInfo, getNotifications, notificationIssueIdsByWorkspaceId, groupedNotifications } = useWorkspaceNotifications(); + const { loader, paginationInfo, getNotifications, notificationIssueIdsByWorkspaceId } = useWorkspaceNotifications(); const notificationIssueIds = notificationIssueIdsByWorkspaceId(workspaceId); const getNextNotifications = async () => { @@ -30,10 +30,10 @@ export const NotificationCardListRoot: FC = observer( if (!workspaceSlug || !workspaceId || !notificationIssueIds) return <>; return ( -
+ <> {notificationIssueIds.map((issueId: string) => ( // - + ))} {/* fetch next page notifications */} @@ -52,6 +52,6 @@ export const NotificationCardListRoot: FC = observer( )} )} -
+ ); }); diff --git a/web/ee/components/workspace-notifications/root.tsx b/web/ee/components/workspace-notifications/root.tsx index a1b3a27a2e..6eea5a16e9 100644 --- a/web/ee/components/workspace-notifications/root.tsx +++ b/web/ee/components/workspace-notifications/root.tsx @@ -20,7 +20,7 @@ import { getNumberCount } from "@/helpers/string.helper"; // hooks import { useWorkspace, useWorkspaceNotifications } from "@/hooks/store"; // plane web components -import { NotificationCardListRoot } from '@/plane-web/components/workspace-notifications' +import { NotificationCardListRoot } from "@/plane-web/components/workspace-notifications"; export const NotificationsSidebar: FC = observer(() => { const { workspaceSlug } = useParams(); @@ -43,11 +43,11 @@ export const NotificationsSidebar: FC = observer(() => { return (
-
+
diff --git a/web/ee/store/workspace-notifications/notifications.store.ts b/web/ee/store/workspace-notifications/notifications.store.ts index 8c2b2fc5e7..1cb386f931 100644 --- a/web/ee/store/workspace-notifications/notifications.store.ts +++ b/web/ee/store/workspace-notifications/notifications.store.ts @@ -5,11 +5,16 @@ import { computedFn } from "mobx-utils"; import { TNotification, TNotificationPaginatedInfo } from "@plane/types"; import { ENotificationLoader, ENotificationQueryParamType } from "@/constants/notification"; import { convertToEpoch } from "@/helpers/date-time.helper"; -import {RootStore} from '@/plane-web/store/root.store' +import { RootStore } from "@/plane-web/store/root.store"; import workspaceNotificationService from "@/services/workspace-notification.service"; -import { IWorkspaceNotificationStore as IWorkspaceNotificationStoreCore , TNotificationLoader, TNotificationQueryParamType, WorkspaceNotificationStore as WorkspaceNotificationStoreCore } from "@/store/notifications/workspace-notifications.store"; +import { + IWorkspaceNotificationStore as IWorkspaceNotificationStoreCore, + TNotificationLoader, + TNotificationQueryParamType, + WorkspaceNotificationStore as WorkspaceNotificationStoreCore, +} from "@/store/notifications/workspace-notifications.store"; -export type TGroupedNotifications = Record +export type TGroupedNotifications = Record; export interface IWorkspaceNotificationStore extends IWorkspaceNotificationStoreCore { // observales @@ -37,25 +42,24 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i * @param { INotification[] } notifications */ groupNotificationsById = action((notifications: TNotification[]) => { - this.groupedNotifications = groupBy(notifications, (n) => n.data?.issue?.id); + this.groupedNotifications = groupBy(notifications, (n) => n.entity_identifier); }); - notificationIssueIdsByWorkspaceId = computedFn((workspaceId: string)=>{ - if(!workspaceId || Object.keys(this.groupedNotifications).length === 0) return undefined; + notificationIssueIdsByWorkspaceId = computedFn((workspaceId: string) => { + if (!workspaceId || Object.keys(this.groupedNotifications).length === 0) return undefined; const groupedNotificationIssueIds = orderBy( Object.keys(this.groupedNotifications), (issueId) => { const notifications = this.groupedNotifications[issueId]; - const latestNotification = orderBy(notifications,(n)=>convertToEpoch(n.created_at),'desc')[0]; - return convertToEpoch(latestNotification.created_at) + const latestNotification = orderBy(notifications, (n) => convertToEpoch(n.created_at), "desc")[0]; + return convertToEpoch(latestNotification.created_at); }, - 'desc' - ) + "desc" + ); return groupedNotificationIssueIds; - }) - + }); /** * @description get all workspace notification @@ -77,7 +81,7 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i const { results, ...paginationInfo } = notificationResponse; runInAction(() => { if (results) { - this.groupNotificationsById(results) + this.groupNotificationsById(results); } set(this, "paginationInfo", paginationInfo); }); @@ -91,5 +95,3 @@ export class WorkspaceNotificationStore extends WorkspaceNotificationStoreCore i } }; } - -