diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index b4fe260f775..e93b5ee3d6d 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -283,10 +283,26 @@ class IssueRelationSerializer(BaseSerializer): ) name = serializers.CharField(source="related_issue.name", read_only=True) relation_type = serializers.CharField(read_only=True) + state_id = serializers.UUIDField(source="related_issue.state.id", read_only=True) + priority = serializers.CharField(source="related_issue.priority", read_only=True) + assignee_ids = serializers.ListField( + child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), + write_only=True, + required=False, + ) class Meta: model = IssueRelation - fields = ["id", "project_id", "sequence_id", "relation_type", "name"] + fields = [ + "id", + "project_id", + "sequence_id", + "relation_type", + "name", + "state_id", + "priority", + "assignee_ids", + ] read_only_fields = ["workspace", "project"] @@ -298,10 +314,26 @@ class RelatedIssueSerializer(BaseSerializer): sequence_id = serializers.IntegerField(source="issue.sequence_id", read_only=True) name = serializers.CharField(source="issue.name", read_only=True) relation_type = serializers.CharField(read_only=True) + state_id = serializers.UUIDField(source="issue.state.id", read_only=True) + priority = serializers.CharField(source="issue.priority", read_only=True) + assignee_ids = serializers.ListField( + child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), + write_only=True, + required=False, + ) class Meta: model = IssueRelation - fields = ["id", "project_id", "sequence_id", "relation_type", "name"] + fields = [ + "id", + "project_id", + "sequence_id", + "relation_type", + "name", + "state_id", + "priority", + "assignee_ids", + ] read_only_fields = ["workspace", "project"] diff --git a/web/core/components/issues/issue-detail-widgets/relations/content.tsx b/web/core/components/issues/issue-detail-widgets/relations/content.tsx index 10b3b6585eb..54517dcc769 100644 --- a/web/core/components/issues/issue-detail-widgets/relations/content.tsx +++ b/web/core/components/issues/issue-detail-widgets/relations/content.tsx @@ -1,8 +1,9 @@ "use client"; import { FC, useState } from "react"; import { observer } from "mobx-react"; +// plane imports import { EIssueServiceType } from "@plane/constants"; -import { TIssue, TIssueRelationIdMap, TIssueServiceType } from "@plane/types"; +import { TIssue, TIssueServiceType } from "@plane/types"; import { Collapsible } from "@plane/ui"; // components import { RelationIssueList } from "@/components/issues"; @@ -11,6 +12,7 @@ import { CreateUpdateIssueModal } from "@/components/issues/issue-modal"; // hooks import { useIssueDetail } from "@/hooks/store"; // Plane-web +import { CreateUpdateEpicModal } from "@/plane-web/components/epics"; import { useTimeLineRelationOptions } from "@/plane-web/components/relations"; import { TIssueRelationTypes } from "@/plane-web/types"; // helper @@ -62,6 +64,7 @@ export const RelationsCollapsibleContent: FC = observer((props) => { // helper const issueOperations = useRelationOperations(); + const epicOperations = useRelationOperations(EIssueServiceType.EPICS); // derived values const relations = getRelationsByIssueId(issueId); @@ -129,7 +132,6 @@ export const RelationsCollapsibleContent: FC = observer((props) => { relationKey={relation.relationKey} issueIds={relation.issueIds} disabled={disabled} - issueOperations={issueOperations} handleIssueCrudState={handleIssueCrudState} issueServiceType={issueServiceType} /> @@ -146,24 +148,44 @@ export const RelationsCollapsibleContent: FC = observer((props) => { toggleDeleteIssueModal(null); }} data={issueCrudState?.delete?.issue as TIssue} - onSubmit={async () => - await issueOperations.remove(workspaceSlug, projectId, issueCrudState?.delete?.issue?.id as string) - } + onSubmit={async () => { + const deleteOperation = !!issueCrudState.delete.issue?.is_epic + ? epicOperations.remove + : issueOperations.remove; + await deleteOperation(workspaceSlug, projectId, issueCrudState?.delete?.issue?.id as string); + }} + isEpic={!!issueCrudState.delete.issue?.is_epic} /> )} {shouldRenderIssueUpdateModal && ( - { - handleIssueCrudState("update", null, null); - toggleCreateIssueModal(false); - }} - data={issueCrudState?.update?.issue ?? undefined} - onSubmit={async (_issue: TIssue) => { - await issueOperations.update(workspaceSlug, projectId, _issue.id, _issue); - }} - /> + <> + {!!issueCrudState?.update?.issue?.is_epic ? ( + { + handleIssueCrudState("update", null, null); + toggleCreateIssueModal(false); + }} + data={issueCrudState?.update?.issue ?? undefined} + onSubmit={async (_issue: TIssue) => { + await epicOperations.update(workspaceSlug, projectId, _issue.id, _issue); + }} + /> + ) : ( + { + handleIssueCrudState("update", null, null); + toggleCreateIssueModal(false); + }} + data={issueCrudState?.update?.issue ?? undefined} + onSubmit={async (_issue: TIssue) => { + await issueOperations.update(workspaceSlug, projectId, _issue.id, _issue); + }} + /> + )} + )} ); diff --git a/web/core/components/issues/issue-detail-widgets/relations/helper.tsx b/web/core/components/issues/issue-detail-widgets/relations/helper.tsx index 4267e9e1a91..83e70d9cf3c 100644 --- a/web/core/components/issues/issue-detail-widgets/relations/helper.tsx +++ b/web/core/components/issues/issue-detail-widgets/relations/helper.tsx @@ -23,6 +23,8 @@ export const useRelationOperations = ( const { updateIssue, removeIssue } = useIssueDetail(issueServiceType); const { captureIssueEvent } = useEventTracker(); const pathname = usePathname(); + // derived values + const entityName = issueServiceType === EIssueServiceType.ISSUES ? "Issue" : "Epic"; const issueOperations: TRelationIssueOperations = useMemo( () => ({ @@ -32,7 +34,7 @@ export const useRelationOperations = ( setToast({ type: TOAST_TYPE.SUCCESS, title: "Link Copied!", - message: "Issue link copied to clipboard.", + message: `${entityName} link copied to clipboard.`, }); }); }, @@ -51,7 +53,7 @@ export const useRelationOperations = ( setToast({ title: "Success!", type: TOAST_TYPE.SUCCESS, - message: "Issue updated successfully", + message: `${entityName} updated successfully`, }); } catch (error) { captureIssueEvent({ @@ -66,7 +68,7 @@ export const useRelationOperations = ( setToast({ title: "Error!", type: TOAST_TYPE.ERROR, - message: "Issue update failed", + message: `${entityName} update failed`, }); } }, diff --git a/web/core/components/issues/relations/issue-list-item.tsx b/web/core/components/issues/relations/issue-list-item.tsx index dae3a208ec3..095dcc9ca06 100644 --- a/web/core/components/issues/relations/issue-list-item.tsx +++ b/web/core/components/issues/relations/issue-list-item.tsx @@ -16,8 +16,8 @@ import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web components import { IssueIdentifier } from "@/plane-web/components/issues"; import { TIssueRelationTypes } from "@/plane-web/types"; -// -import { TRelationIssueOperations } from "../issue-detail-widgets/relations/helper"; +// local imports +import { useRelationOperations } from "../issue-detail-widgets/relations/helper"; type Props = { workspaceSlug: string; @@ -26,7 +26,6 @@ type Props = { relationKey: TIssueRelationTypes; relationIssueId: string; disabled: boolean; - issueOperations: TRelationIssueOperations; handleIssueCrudState: (key: "update" | "delete", issueId: string, issue?: TIssue | null) => void; issueServiceType?: TIssueServiceType; }; @@ -39,7 +38,6 @@ export const RelationIssueListItem: FC = observer((props) => { relationKey, relationIssueId, disabled = false, - issueOperations, handleIssueCrudState, issueServiceType = EIssueServiceType.ISSUES, } = props; @@ -57,6 +55,7 @@ export const RelationIssueListItem: FC = observer((props) => { // derived values const issue = getIssueById(relationIssueId); const { handleRedirection } = useIssuePeekOverviewRedirection(!!issue?.is_epic); + const issueOperations = useRelationOperations(!!issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES); const projectDetail = (issue && issue.project_id && project.getProjectById(issue.project_id)) || undefined; const currentIssueStateDetail = (issue?.project_id && getProjectStates(issue?.project_id)?.find((state) => issue?.state_id == state.id)) || @@ -134,62 +133,58 @@ export const RelationIssueListItem: FC = observer((props) => { {issue.name} - {!issue.is_epic && ( - <> -
{ - e.preventDefault(); - e.stopPropagation(); - }} - > - -
-
- - {!disabled && ( - -
- - Edit issue -
-
- )} +
{ + e.preventDefault(); + e.stopPropagation(); + }} + > + +
+
+ + {!disabled && ( + +
+ + Edit +
+
+ )} - -
- - Copy issue link -
-
+ +
+ + Copy link +
+
- {!disabled && ( - -
- - Remove relation -
-
- )} + {!disabled && ( + +
+ + Remove relation +
+
+ )} - {!disabled && ( - -
- - Delete issue -
-
- )} -
-
- - )} + {!disabled && ( + +
+ + Delete +
+
+ )} +
+
)} diff --git a/web/core/components/issues/relations/issue-list.tsx b/web/core/components/issues/relations/issue-list.tsx index efd8786d986..947916c292e 100644 --- a/web/core/components/issues/relations/issue-list.tsx +++ b/web/core/components/issues/relations/issue-list.tsx @@ -8,8 +8,6 @@ import { TIssue, TIssueServiceType } from "@plane/types"; import { RelationIssueListItem } from "@/components/issues/relations"; // Plane-web import { TIssueRelationTypes } from "@/plane-web/types"; -// -import { TRelationIssueOperations } from "../issue-detail-widgets/relations/helper"; type Props = { workspaceSlug: string; @@ -17,7 +15,6 @@ type Props = { issueId: string; issueIds: string[]; relationKey: TIssueRelationTypes; - issueOperations: TRelationIssueOperations; handleIssueCrudState: (key: "update" | "delete", issueId: string, issue?: TIssue | null) => void; disabled?: boolean; issueServiceType?: TIssueServiceType; @@ -31,7 +28,6 @@ export const RelationIssueList: FC = observer((props) => { issueIds, relationKey, disabled = false, - issueOperations, handleIssueCrudState, issueServiceType = EIssueServiceType.ISSUES, } = props; @@ -50,7 +46,6 @@ export const RelationIssueList: FC = observer((props) => { relationIssueId={relationIssueId} disabled={disabled} handleIssueCrudState={handleIssueCrudState} - issueOperations={issueOperations} issueServiceType={issueServiceType} /> ))}