import { FC, useCallback, useEffect, useState } from "react"; import { observer } from "mobx-react"; import { useRouter } from "next/router"; import { ChevronDown, ChevronUp, Clock, ExternalLink, FileStack, Link, Trash2 } from "lucide-react"; import { Button, ControlLink, CustomMenu, TOAST_TYPE, setToast } from "@plane/ui"; // components import { AcceptIssueModal, DeclineIssueModal, DeleteInboxIssueModal, InboxIssueSnoozeModal, InboxIssueStatus, SelectDuplicateInboxIssueModal, } from "@/components/inbox"; import { IssueUpdateStatus } from "@/components/issues"; // constants import { EUserProjectRoles } from "@/constants/project"; // helpers import { copyUrlToClipboard } from "@/helpers/string.helper"; // hooks import { useUser, useProjectInbox, useProject } from "@/hooks/store"; // store types import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store"; type TInboxIssueActionsHeader = { workspaceSlug: string; projectId: string; inboxIssue: IInboxIssueStore | undefined; isSubmitting: "submitting" | "submitted" | "saved"; }; export const InboxIssueActionsHeader: FC = observer((props) => { const { workspaceSlug, projectId, inboxIssue, isSubmitting } = props; // states const [isSnoozeDateModalOpen, setIsSnoozeDateModalOpen] = useState(false); const [selectDuplicateIssue, setSelectDuplicateIssue] = useState(false); const [acceptIssueModal, setAcceptIssueModal] = useState(false); const [declineIssueModal, setDeclineIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false); // store const { deleteInboxIssue, inboxIssuesArray } = useProjectInbox(); const { currentUser, membership: { currentProjectRole }, } = useUser(); const router = useRouter(); const { getProjectById } = useProject(); const issue = inboxIssue?.issue; // derived values const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; const canMarkAsDuplicate = isAllowed && inboxIssue?.status === -2; const canMarkAsAccepted = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2); const canMarkAsDeclined = isAllowed && inboxIssue?.status === -2; const canDelete = isAllowed || inboxIssue?.created_by === currentUser?.id; const isCompleted = inboxIssue?.status === 1; const currentInboxIssueId = inboxIssue?.issue?.id; const issueLink = `${workspaceSlug}/projects/${issue?.project_id}/issues/${currentInboxIssueId}`; const handleInboxIssueAccept = async () => { inboxIssue?.updateInboxIssueStatus(1); setAcceptIssueModal(false); }; const handleInboxIssueDecline = async () => { inboxIssue?.updateInboxIssueStatus(-1); setDeclineIssueModal(false); }; const handleInboxIssueDuplicate = (issueId: string) => { inboxIssue?.updateInboxIssueDuplicateTo(issueId); }; const handleInboxSIssueSnooze = async (date: Date) => { inboxIssue?.updateInboxIssueSnoozeTill(date); setIsSnoozeDateModalOpen(false); }; const handleInboxIssueDelete = async () => { if (!inboxIssue || !currentInboxIssueId) return; deleteInboxIssue(workspaceSlug, projectId, currentInboxIssueId).finally(() => { router.push(`/${workspaceSlug}/projects/${projectId}/inbox`); }); }; const handleCopyIssueLink = () => copyUrlToClipboard(issueLink).then(() => setToast({ type: TOAST_TYPE.SUCCESS, title: "Link copied", message: "Issue link copied to clipboard", }) ); const currentIssueIndex = inboxIssuesArray.findIndex((issue) => issue.issue.id === currentInboxIssueId) ?? 0; const handleInboxIssueNavigation = useCallback( (direction: "next" | "prev") => { if (!inboxIssuesArray || !currentInboxIssueId) return; const activeElement = document.activeElement as HTMLElement; if (activeElement && (activeElement.classList.contains("tiptap") || activeElement.id === "title-input")) return; const nextIssueIndex = direction === "next" ? (currentIssueIndex + 1) % inboxIssuesArray.length : (currentIssueIndex - 1 + inboxIssuesArray.length) % inboxIssuesArray.length; const nextIssueId = inboxIssuesArray[nextIssueIndex].issue.id; if (!nextIssueId) return; router.push(`/${workspaceSlug}/projects/${projectId}/inbox?inboxIssueId=${nextIssueId}`); }, [currentInboxIssueId, currentIssueIndex, inboxIssuesArray, projectId, router, workspaceSlug] ); const onKeyDown = useCallback( (e: KeyboardEvent) => { if (e.key === "ArrowUp") { handleInboxIssueNavigation("prev"); } else if (e.key === "ArrowDown") { handleInboxIssueNavigation("next"); } }, [handleInboxIssueNavigation] ); useEffect(() => { document.addEventListener("keydown", onKeyDown); return () => { document.removeEventListener("keydown", onKeyDown); }; }, [onKeyDown]); if (!inboxIssue) return null; return ( <> <> setSelectDuplicateIssue(false)} value={inboxIssue?.duplicate_to} onSubmit={handleInboxIssueDuplicate} /> setAcceptIssueModal(false)} onSubmit={handleInboxIssueAccept} /> setDeclineIssueModal(false)} onSubmit={handleInboxIssueDecline} /> setDeleteIssueModal(false)} onSubmit={handleInboxIssueDelete} /> setIsSnoozeDateModalOpen(false)} value={inboxIssue?.snoozed_till} onConfirm={handleInboxSIssueSnooze} />
{issue?.project_id && issue.sequence_id && (

{getProjectById(issue.project_id)?.identifier}-{issue.sequence_id}

)}
{canMarkAsAccepted && (
)} {canMarkAsDeclined && (
)} {isCompleted ? (
router.push(`/${workspaceSlug}/projects/${issue?.project_id}/issues/${currentInboxIssueId}`) } >
) : ( {canMarkAsAccepted && ( setIsSnoozeDateModalOpen(true)}>
Snooze
)} {canMarkAsDuplicate && ( setSelectDuplicateIssue(true)}>
Mark as duplicate
)} {canDelete && ( setDeleteIssueModal(true)}>
Delete
)}
)}
); });