"use client"; import { Dispatch, MouseEvent, SetStateAction, useEffect, useRef } from "react"; import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; import { draggable } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; import { observer } from "mobx-react-lite"; import { ChevronRight } from "lucide-react"; // types import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types"; // ui import { Spinner, Tooltip, ControlLink, setToast, TOAST_TYPE } from "@plane/ui"; // components import { MultipleSelectEntityAction } from "@/components/core"; import { IssueProperties } from "@/components/issues/issue-layouts/properties"; // helpers import { cn } from "@/helpers/common.helper"; // hooks import { useAppRouter, useIssueDetail, useProject } from "@/hooks/store"; import { TSelectionHelper } from "@/hooks/use-multiple-select"; import { usePlatformOS } from "@/hooks/use-platform-os"; // types import { TRenderQuickActions } from "./list-view-types"; interface IssueBlockProps { issueId: string; issuesMap: TIssueMap; groupId: string; updateIssue: ((projectId: string, issueId: string, data: Partial) => Promise) | undefined; quickActions: TRenderQuickActions; displayProperties: IIssueDisplayProperties | undefined; canEditProperties: (projectId: string | undefined) => boolean; nestingLevel: number; spacingLeft?: number; isExpanded: boolean; setExpanded: Dispatch>; selectionHelpers: TSelectionHelper; isCurrentBlockDragging: boolean; setIsCurrentBlockDragging: React.Dispatch>; canDrag: boolean; } export const IssueBlock = observer((props: IssueBlockProps) => { const { issuesMap, issueId, groupId, updateIssue, quickActions, displayProperties, canEditProperties, nestingLevel, spacingLeft = 14, isExpanded, setExpanded, selectionHelpers, isCurrentBlockDragging, setIsCurrentBlockDragging, canDrag, } = props; // ref const issueRef = useRef(null); // hooks const { workspaceSlug, projectId } = useAppRouter(); const { getProjectIdentifierById } = useProject(); const { getIsIssuePeeked, peekIssue, setPeekIssue, subIssues: subIssuesStore } = useIssueDetail(); const handleIssuePeekOverview = (issue: TIssue) => workspaceSlug && issue && issue.project_id && issue.id && !getIsIssuePeeked(issue.id) && setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id, nestingLevel: nestingLevel }); const issue = issuesMap[issueId]; const subIssuesCount = issue?.sub_issues_count ?? 0; const { isMobile } = usePlatformOS(); useEffect(() => { const element = issueRef.current; if (!element) return; return combine( draggable({ element, canDrag: () => canDrag, getInitialData: () => ({ id: issueId, type: "ISSUE", groupId }), onDragStart: () => { setIsCurrentBlockDragging(true); }, onDrop: () => { setIsCurrentBlockDragging(false); }, }) ); }, [canDrag, issueId, groupId, setIsCurrentBlockDragging]); if (!issue) return null; const canEditIssueProperties = canEditProperties(issue.project_id); const projectIdentifier = getProjectIdentifierById(issue.project_id); const isIssueSelected = selectionHelpers.getIsEntitySelected(issue.id); const isIssueActive = selectionHelpers.getIsEntityActive(issue.id); const isSubIssue = nestingLevel !== 0; const canSelectIssues = canEditIssueProperties && !selectionHelpers.isSelectionDisabled; const marginLeft = `${spacingLeft}px`; const handleToggleExpand = (e: MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (nestingLevel >= 3) { handleIssuePeekOverview(issue); } else { setExpanded((prevState) => { if (!prevState && workspaceSlug && issue) subIssuesStore.fetchSubIssues(workspaceSlug.toString(), issue.project_id, issue.id); return !prevState; }); } }; //TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier const keyMinWidth = ((projectIdentifier?.length ?? 0) + 5) * 7; return (
{ if (!canDrag) { setToast({ type: TOAST_TYPE.WARNING, title: "Cannot move issue", message: "Drag and drop is disabled for the current grouping", }); } }} >
{/* select checkbox */} {projectId && canSelectIssues && ( Only issues within the current
project can be selected. } disabled={issue.project_id === projectId} >
)} {displayProperties && displayProperties?.key && (
{projectIdentifier}-{issue.sequence_id}
)} {/* sub-issues chevron */}
{subIssuesCount > 0 && ( )}
{issue?.tempId !== undefined && (
)}
{issue?.is_draft ? (

{issue.name}

) : ( handleIssuePeekOverview(issue)} className="w-full truncate cursor-pointer text-sm text-custom-text-100" disabled={!!issue?.tempId} >

{issue.name}

)}
{!issue?.tempId && (
{quickActions({ issue, parentRef: issueRef, })}
)}
{!issue?.tempId ? ( <>
{quickActions({ issue, parentRef: issueRef, })}
) : (
)}
); });