plane/web/components/inbox/sidebar/root.tsx
guru_sainath 20b0edeaa6
[WEB-999] chore: updated UI improvements and workflow updates in the project inbox (#4180)
* chore: snoozed filter in the issue inbox filter

* chore: navigating to the next or previous issue when we accept, decline, or duplicate the issue in inbox

* chore: Implemented state, label, assignee and target_date in the inbox issue description and Implemented issue edit confirmation once we click accept the inbox issue

* chore: removed logs

* chore: inbox issue create response

* chore: update inbox issue response

* chore: updated inbox issue accept workflow and added issue properties in inbox issue create modal

* chore: resolved build errors and upgraded lucide react

* chore: updated inbox issue store hook

* chore: code cleanup and removed validation for inbox description

* fix: renamed the variable isLoading to loader in project-inbox store

* fix: updated set function for issue property update

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
2024-04-15 12:49:14 +05:30

146 lines
5.2 KiB
TypeScript

import { FC, useCallback, useRef } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { TInboxIssueCurrentTab } from "@plane/types";
import { Loader } from "@plane/ui";
// components
import { EmptyState } from "@/components/empty-state";
import { FiltersRoot, InboxIssueAppliedFilters, InboxIssueList } from "@/components/inbox";
import { InboxSidebarLoader } from "@/components/ui";
// constants
import { EmptyStateType } from "@/constants/empty-state";
// helpers
import { cn } from "@/helpers/common.helper";
import { EInboxIssueCurrentTab } from "@/helpers/inbox.helper";
// hooks
import { useProject, useProjectInbox } from "@/hooks/store";
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
type IInboxSidebarProps = {
workspaceSlug: string;
projectId: string;
};
const tabNavigationOptions: { key: TInboxIssueCurrentTab; label: string }[] = [
{
key: EInboxIssueCurrentTab.OPEN,
label: "Open",
},
{
key: EInboxIssueCurrentTab.CLOSED,
label: "Closed",
},
];
export const InboxSidebar: FC<IInboxSidebarProps> = observer((props) => {
const { workspaceSlug, projectId } = props;
// ref
const containerRef = useRef<HTMLDivElement>(null);
const elementRef = useRef<HTMLDivElement>(null);
// store
const { currentProjectDetails } = useProject();
const {
currentTab,
handleCurrentTab,
loader,
inboxIssuesArray,
inboxIssuePaginationInfo,
fetchInboxPaginationIssues,
getAppliedFiltersCount,
} = useProjectInbox();
const router = useRouter();
const fetchNextPages = useCallback(() => {
if (!workspaceSlug || !projectId) return;
fetchInboxPaginationIssues(workspaceSlug.toString(), projectId.toString());
}, [workspaceSlug, projectId, fetchInboxPaginationIssues]);
// page observer
useIntersectionObserver({
containerRef,
elementRef,
callback: fetchNextPages,
rootMargin: "20%",
});
return (
<div className="flex-shrink-0 w-2/6 h-full border-r border-custom-border-300">
<div className="relative w-full h-full flex flex-col overflow-hidden">
<div className="border-b border-custom-border-300 flex-shrink-0 w-full h-[50px] relative flex items-center gap-2 pr-3 whitespace-nowrap">
{tabNavigationOptions.map((option) => (
<div
key={option?.key}
className={cn(
`text-sm relative flex items-center gap-1 h-[50px] px-2 cursor-pointer transition-all font-medium`,
currentTab === option?.key ? `text-custom-primary-100` : `hover:text-custom-text-200`
)}
onClick={() => {
if (currentTab != option?.key) handleCurrentTab(option?.key);
router.push(`/${workspaceSlug}/projects/${projectId}/inbox?currentTab=${option?.key}`);
}}
>
<div>{option?.label}</div>
{option?.key === "open" && currentTab === option?.key && (
<div className="rounded-full p-1.5 py-0.5 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold">
{inboxIssuePaginationInfo?.total_results || 0}
</div>
)}
<div
className={cn(
`border absolute bottom-0 right-0 left-0 rounded-t-md`,
currentTab === option?.key ? `border-custom-primary-100` : `border-transparent`
)}
/>
</div>
))}
<div className="ml-auto">
<FiltersRoot />
</div>
</div>
<InboxIssueAppliedFilters />
{loader != undefined && loader === "filter-loading" && !inboxIssuePaginationInfo?.next_page_results ? (
<InboxSidebarLoader />
) : (
<div
className="w-full h-full overflow-hidden overflow-y-auto vertical-scrollbar scrollbar-md"
ref={containerRef}
>
{inboxIssuesArray.length > 0 ? (
<InboxIssueList
workspaceSlug={workspaceSlug}
projectId={projectId}
projectIdentifier={currentProjectDetails?.identifier}
inboxIssues={inboxIssuesArray}
/>
) : (
<div className="flex items-center justify-center h-full w-full">
<EmptyState
type={
getAppliedFiltersCount > 0
? EmptyStateType.INBOX_SIDEBAR_FILTER_EMPTY_STATE
: currentTab === EInboxIssueCurrentTab.OPEN
? EmptyStateType.INBOX_SIDEBAR_OPEN_TAB
: EmptyStateType.INBOX_SIDEBAR_CLOSED_TAB
}
layout="screen-simple"
/>
</div>
)}
<div ref={elementRef}>
{inboxIssuePaginationInfo?.next_page_results && (
<Loader className="mx-auto w-full space-y-4 py-4 px-2">
<Loader.Item height="64px" width="w-100" />
<Loader.Item height="64px" width="w-100" />
</Loader>
)}
</div>
</div>
)}
</div>
</div>
);
});