diff --git a/apps/web/ce/components/issues/issue-details/issue-identifier.tsx b/apps/web/ce/components/issues/issue-details/issue-identifier.tsx index c81b0075b5..d734ddb70f 100644 --- a/apps/web/ce/components/issues/issue-details/issue-identifier.tsx +++ b/apps/web/ce/components/issues/issue-details/issue-identifier.tsx @@ -36,7 +36,7 @@ type TIssueTypeIdentifier = { size?: "xs" | "sm" | "md" | "lg"; }; -export const IssueTypeIdentifier: FC = observer((props) => <>); +export const IssueTypeIdentifier: FC = observer((_props) => <>); type TIdentifierTextProps = { identifier: string; @@ -94,7 +94,7 @@ export const IssueIdentifier: React.FC = observer((props) if (!shouldRenderIssueID) return null; return ( -
+
handleIssuePeekOverview(issue)} - className="block w-full text-sm text-custom-text-100 rounded border-b md:border-[1px] border-custom-border-200 hover:border-custom-border-400" - disabled={!!issue?.tempId || isMobile} - ref={ref} - > - <> - {issue?.tempId !== undefined && ( -
- )} - -
-
- - {issue.project_id && ( - - )} - -
{issue.name}
-
-
-
{ - e.preventDefault(); - e.stopPropagation(); - }} + + handleIssuePeekOverview(issue)} + className="block w-full text-sm text-custom-text-100 rounded border-b md:border-[1px] border-custom-border-200 hover:border-custom-border-400" + disabled={!!issue?.tempId || isMobile} + ref={ref} > - {quickActions({ - issue, - parentRef: blockRef, - customActionButton, - placement, - })} -
-
- - + <> + {issue?.tempId !== undefined && ( +
+ )} + +
+
+ + {issue.project_id && ( + + )} +
{issue.name}
+
+
{ + e.preventDefault(); + e.stopPropagation(); + }} + > + {quickActions({ + issue, + parentRef: blockRef, + customActionButton, + placement, + })} +
+
+ + + } + /> + + <> + {issue.project_id && ( + + )} + + + ); }) ); diff --git a/apps/web/core/components/issues/issue-layouts/gantt/blocks.tsx b/apps/web/core/components/issues/issue-layouts/gantt/blocks.tsx index 19bef233de..79524f9d19 100644 --- a/apps/web/core/components/issues/issue-layouts/gantt/blocks.tsx +++ b/apps/web/core/components/issues/issue-layouts/gantt/blocks.tsx @@ -2,13 +2,13 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -// ui +// plane imports +import { Popover } from "@plane/propel/popover"; import { Tooltip } from "@plane/propel/tooltip"; import { ControlLink } from "@plane/ui"; import { findTotalDaysInRange, generateWorkItemLink } from "@plane/utils"; // components import { SIDEBAR_WIDTH } from "@/components/gantt-chart/constants"; -// helpers // hooks import { useIssueDetail } from "@/hooks/store/use-issue-detail"; import { useIssues } from "@/hooks/store/use-issues"; @@ -17,10 +17,11 @@ import { useProjectState } from "@/hooks/store/use-project-state"; import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; import useIssuePeekOverviewRedirection from "@/hooks/use-issue-peek-overview-redirection"; import { usePlatformOS } from "@/hooks/use-platform-os"; -// plane web components +// plane web imports import { IssueIdentifier } from "@/plane-web/components/issues/issue-details/issue-identifier"; -// import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats"; +// local imports +import { WorkItemPreviewCard } from "../../preview-card"; import { getBlockViewDetails } from "../utils"; import type { GanttStoreType } from "./base-gantt-root"; @@ -48,46 +49,54 @@ export const IssueGanttBlock: React.FC = observer((props) => { const stateDetails = issueDetails && getProjectStates(issueDetails?.project_id)?.find((state) => state?.id == issueDetails?.state_id); - const { message, blockStyle } = getBlockViewDetails(issueDetails, stateDetails?.color ?? ""); + const { blockStyle } = getBlockViewDetails(issueDetails, stateDetails?.color ?? ""); const handleIssuePeekOverview = () => handleRedirection(workspaceSlug, issueDetails, isMobile); const duration = findTotalDaysInRange(issueDetails?.start_date, issueDetails?.target_date) || 0; return ( - -
{issueDetails?.name}
-
{message}
-
- } - position="top-start" - disabled={!message} - > -
-
-
- {issueDetails?.name} -
- {isEpic && ( - = 2} - /> - )} -
- + + +
+
+ {issueDetails?.name} +
+ {isEpic && ( + = 2} + /> + )} +
+ } + /> + + <> + {issueDetails && issueDetails?.project_id && ( + + )} + + +
); }); diff --git a/apps/web/core/components/issues/preview-card/date.tsx b/apps/web/core/components/issues/preview-card/date.tsx new file mode 100644 index 0000000000..38f08b500e --- /dev/null +++ b/apps/web/core/components/issues/preview-card/date.tsx @@ -0,0 +1,51 @@ +import { CalendarDays } from "lucide-react"; +// plane imports +import { DueDatePropertyIcon, StartDatePropertyIcon } from "@plane/propel/icons"; +import type { TStateGroups } from "@plane/types"; +import { cn, renderFormattedDate, shouldHighlightIssueDueDate } from "@plane/utils"; + +type Props = { + startDate: string | null; + stateGroup: TStateGroups; + targetDate: string | null; +}; + +export const WorkItemPreviewCardDate: React.FC = (props) => { + const { startDate, stateGroup, targetDate } = props; + // derived values + const isDateRangeEnabled = Boolean(startDate && targetDate); + const shouldHighlightDate = shouldHighlightIssueDueDate(targetDate, stateGroup); + + if (!startDate && !targetDate) return null; + + return ( +
+ {isDateRangeEnabled ? ( +
+ + + {renderFormattedDate(startDate)} - {renderFormattedDate(targetDate)} + +
+ ) : startDate ? ( +
+ + {renderFormattedDate(startDate)} +
+ ) : ( +
+ + {renderFormattedDate(targetDate)} +
+ )} +
+ ); +}; diff --git a/apps/web/core/components/issues/preview-card/index.ts b/apps/web/core/components/issues/preview-card/index.ts new file mode 100644 index 0000000000..1efe34c51e --- /dev/null +++ b/apps/web/core/components/issues/preview-card/index.ts @@ -0,0 +1 @@ +export * from "./root"; diff --git a/apps/web/core/components/issues/preview-card/root.tsx b/apps/web/core/components/issues/preview-card/root.tsx new file mode 100644 index 0000000000..90432e7ccc --- /dev/null +++ b/apps/web/core/components/issues/preview-card/root.tsx @@ -0,0 +1,63 @@ +import { observer } from "mobx-react"; +// plane imports +import { PriorityIcon, StateGroupIcon } from "@plane/propel/icons"; +import type { TIssue, TStateGroups } from "@plane/types"; +// hooks +import { useProject } from "@/hooks/store/use-project"; +import { useProjectState } from "@/hooks/store/use-project-state"; +// plane web imports +import { IssueIdentifier } from "@/plane-web/components/issues/issue-details/issue-identifier"; +// local imports +import { WorkItemPreviewCardDate } from "./date"; + +type Props = { + projectId: string; + stateDetails: { + group?: TStateGroups; + id?: string; + name?: string; + }; + workItem: Pick; +}; + +export const WorkItemPreviewCard: React.FC = observer((props) => { + const { projectId, stateDetails, workItem } = props; + // store hooks + const { getProjectIdentifierById } = useProject(); + const { getStateById } = useProjectState(); + // derived values + const projectIdentifier = getProjectIdentifierById(projectId); + const fallbackStateDetails = stateDetails.id ? getStateById(stateDetails.id) : undefined; + const stateGroup = stateDetails?.group ?? fallbackStateDetails?.group ?? "backlog"; + const stateName = stateDetails?.name ?? fallbackStateDetails?.name; + + return ( +
+
+ +
+ +

{stateName}

+
+
+
+
{workItem.name}
+
+
+ + +
+
+ ); +});