[WEB-3396] chore: work items parent select improvement (#6608)

* chore: work items parent select improvements

* chore: code refactor
This commit is contained in:
Anmol Singh Bhatia 2025-02-15 05:05:37 +05:30 committed by GitHub
parent 4353cc0c4a
commit 8a792d381b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 120 additions and 59 deletions

View file

@ -2,3 +2,4 @@ export * from "./issue-identifier";
export * from "./issue-properties-activity";
export * from "./issue-type-switcher";
export * from "./issue-type-activity";
export * from "./parent-select-root";

View file

@ -0,0 +1,82 @@
"use client";
import React from "react";
import { observer } from "mobx-react";
import { useTranslation } from "@plane/i18n";
// ui
import { TOAST_TYPE, setToast } from "@plane/ui";
// components
import { IssueParentSelect, TIssueOperations } from "@/components/issues";
// hooks
import { useIssueDetail } from "@/hooks/store";
type TIssueParentSelect = {
className?: string;
disabled?: boolean;
issueId: string;
issueOperations: TIssueOperations;
projectId: string;
workspaceSlug: string;
};
export const IssueParentSelectRoot: React.FC<TIssueParentSelect> = observer((props) => {
const { issueId, issueOperations, projectId, workspaceSlug } = props;
const { t } = useTranslation();
// store hooks
const {
issue: { getIssueById },
} = useIssueDetail();
const {
toggleParentIssueModal,
removeSubIssue,
subIssues: { setSubIssueHelpers, fetchSubIssues },
} = useIssueDetail();
// derived values
const issue = getIssueById(issueId);
const parentIssue = issue?.parent_id ? getIssueById(issue.parent_id) : undefined;
const handleParentIssue = async (_issueId: string | null = null) => {
try {
await issueOperations.update(workspaceSlug, projectId, issueId, { parent_id: _issueId });
await issueOperations.fetch(workspaceSlug, projectId, issueId, false);
if (_issueId) await fetchSubIssues(workspaceSlug, projectId, _issueId);
toggleParentIssueModal(null);
} catch (error) {
console.error("something went wrong while fetching the issue");
}
};
const handleRemoveSubIssue = async (
workspaceSlug: string,
projectId: string,
parentIssueId: string,
issueId: string
) => {
try {
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
await removeSubIssue(workspaceSlug, projectId, parentIssueId, issueId);
await fetchSubIssues(workspaceSlug, projectId, parentIssueId);
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
} catch (error) {
setToast({
type: TOAST_TYPE.ERROR,
title: t("common.error.label"),
message: t("common.something_went_wrong"),
});
}
};
const workItemLink = `/${workspaceSlug}/projects/${parentIssue?.project_id}/issues/${parentIssue?.id}`;
if (!issue) return <></>;
return (
<IssueParentSelect
{...props}
handleParentIssue={handleParentIssue}
handleRemoveSubIssue={handleRemoveSubIssue}
workItemLink={workItemLink}
/>
);
});

View file

@ -6,7 +6,7 @@ import Link from "next/link";
import { Pencil, X } from "lucide-react";
import { useTranslation } from "@plane/i18n";
// ui
import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
import { Tooltip } from "@plane/ui";
// components
import { ParentIssuesListModal } from "@/components/issues";
// helpers
@ -17,31 +17,41 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { IssueIdentifier } from "@/plane-web/components/issues";
// types
import { TIssueOperations } from "./root";
type TIssueParentSelect = {
className?: string;
disabled?: boolean;
issueId: string;
issueOperations: TIssueOperations;
projectId: string;
workspaceSlug: string;
handleParentIssue: (_issueId?: string | null) => Promise<void>;
handleRemoveSubIssue: (
workspaceSlug: string,
projectId: string,
parentIssueId: string,
issueId: string
) => Promise<void>;
workItemLink: string;
};
export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props) => {
const { className = "", disabled = false, issueId, issueOperations, projectId, workspaceSlug } = props;
const {
className = "",
disabled = false,
issueId,
projectId,
workspaceSlug,
handleParentIssue,
handleRemoveSubIssue,
workItemLink,
} = props;
const { t } = useTranslation();
// store hooks
const { getProjectById } = useProject();
const {
issue: { getIssueById },
} = useIssueDetail();
const {
isParentIssueModalOpen,
toggleParentIssueModal,
removeSubIssue,
subIssues: { setSubIssueHelpers, fetchSubIssues },
} = useIssueDetail();
const { isParentIssueModalOpen, toggleParentIssueModal } = useIssueDetail();
// derived values
const issue = getIssueById(issueId);
@ -49,36 +59,6 @@ export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props)
const parentIssueProjectDetails =
parentIssue && parentIssue.project_id ? getProjectById(parentIssue.project_id) : undefined;
const { isMobile } = usePlatformOS();
const handleParentIssue = async (_issueId: string | null = null) => {
try {
await issueOperations.update(workspaceSlug, projectId, issueId, { parent_id: _issueId });
await issueOperations.fetch(workspaceSlug, projectId, issueId, false);
_issueId && (await fetchSubIssues(workspaceSlug, projectId, _issueId));
toggleParentIssueModal(null);
} catch (error) {
console.error("something went wrong while fetching the issue");
}
};
const handleRemoveSubIssue = async (
workspaceSlug: string,
projectId: string,
parentIssueId: string,
issueId: string
) => {
try {
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
await removeSubIssue(workspaceSlug, projectId, parentIssueId, issueId);
await fetchSubIssues(workspaceSlug, projectId, parentIssueId);
setSubIssueHelpers(parentIssueId, "issue_loader", issueId);
} catch (error) {
setToast({
type: TOAST_TYPE.ERROR,
title: t("common.error.label"),
message: t("common.something_went_wrong"),
});
}
};
if (!issue) return <></>;
@ -109,12 +89,7 @@ export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props)
{issue.parent_id && parentIssue ? (
<div className="flex items-center gap-1 bg-green-500/20 rounded px-1.5 py-1">
<Tooltip tooltipHeading="Title" tooltipContent={parentIssue.name} isMobile={isMobile}>
<Link
href={`/${workspaceSlug}/projects/${parentIssue.project_id}/issues/${parentIssue?.id}`}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
<Link href={workItemLink} target="_blank" rel="noopener noreferrer" onClick={(e) => e.stopPropagation()}>
{parentIssue?.project_id && parentIssueProjectDetails && (
<IssueIdentifier
projectId={parentIssue.project_id}

View file

@ -4,7 +4,14 @@ import { FC, useMemo } from "react";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
// types
import { EIssuesStoreType, ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED,EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import {
EIssuesStoreType,
ISSUE_UPDATED,
ISSUE_DELETED,
ISSUE_ARCHIVED,
EUserPermissions,
EUserPermissionsLevel,
} from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { TIssue } from "@plane/types";
// ui

View file

@ -16,7 +16,7 @@ import {
StateDropdown,
} from "@/components/dropdowns";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import { IssueCycleSelect, IssueLabel, IssueModuleSelect, IssueParentSelect } from "@/components/issues";
import { IssueCycleSelect, IssueLabel, IssueModuleSelect } from "@/components/issues";
// helpers
import { cn } from "@/helpers/common.helper";
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
@ -25,7 +25,7 @@ import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
import { useProjectEstimates, useIssueDetail, useProject, useProjectState, useMember } from "@/hooks/store";
// plane web components
import { IssueAdditionalPropertyValuesUpdate } from "@/plane-web/components/issue-types/values";
import { IssueWorklogProperty } from "@/plane-web/components/issues";
import { IssueParentSelectRoot, IssueWorklogProperty } from "@/plane-web/components/issues";
// components
import type { TIssueOperations } from "./root";
@ -261,7 +261,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
<LayoutPanelTop className="h-4 w-4 flex-shrink-0" />
<span>{t("common.parent")}</span>
</div>
<IssueParentSelect
<IssueParentSelectRoot
className="h-full w-3/5 flex-grow"
workspaceSlug={workspaceSlug}
projectId={projectId}

View file

@ -16,13 +16,7 @@ import {
StateDropdown,
} from "@/components/dropdowns";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import {
IssueCycleSelect,
IssueModuleSelect,
IssueParentSelect,
IssueLabel,
TIssueOperations,
} from "@/components/issues";
import { IssueCycleSelect, IssueModuleSelect, IssueLabel, TIssueOperations } from "@/components/issues";
// helpers
import { cn } from "@/helpers/common.helper";
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
@ -30,7 +24,7 @@ import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
import { useIssueDetail, useMember, useProject, useProjectState } from "@/hooks/store";
// plane web components
import { IssueAdditionalPropertyValuesUpdate } from "@/plane-web/components/issue-types/values";
import { IssueWorklogProperty } from "@/plane-web/components/issues";
import { IssueParentSelectRoot, IssueWorklogProperty } from "@/plane-web/components/issues";
interface IPeekOverviewProperties {
workspaceSlug: string;
@ -269,7 +263,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
<LayoutPanelTop className="h-4 w-4 flex-shrink-0" />
<p>{t("common.parent")}</p>
</div>
<IssueParentSelect
<IssueParentSelectRoot
className="w-3/4 flex-grow h-full"
disabled={disabled}
issueId={issueId}

View file

@ -2,3 +2,4 @@ export * from "./issue-identifier";
export * from "./issue-properties-activity";
export * from "./issue-type-switcher";
export * from "./issue-type-activity";
export * from "./parent-select-root";

View file

@ -0,0 +1 @@
export * from "ce/components/issues/issue-details/parent-select-root";