mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
f45c2d12fd
* chore: inbox duplicate issue modal improvement * chore: handled tab navigation in inbox issues and handled cross project inbox issues * chore: fetch inbox issue activity once the issue is updated in inbox issue * chore: disable duplicate inbox issue actions * chore: duplicate issue mutation in the inbox issue * chore: inbox create modal sidebar tab change updated * chore: multiple date selection in the inbox issue filters * chore: code refactor * chore: removed project dependancy on the inbox store structure --------- Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
145 lines
5.0 KiB
TypeScript
145 lines
5.0 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";
|
|
// 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: "open",
|
|
label: "Open",
|
|
},
|
|
{
|
|
key: "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,
|
|
isLoading,
|
|
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 />
|
|
|
|
{isLoading === "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 === "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>
|
|
);
|
|
});
|