[WEB-3207] chore: add state_id, priority and assignee_ids to create issue relation response (#6448)
This commit is contained in:
parent
586a320d86
commit
0b53912295
5 changed files with 129 additions and 83 deletions
|
|
@ -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"]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Props> = 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<Props> = observer((props) => {
|
|||
relationKey={relation.relationKey}
|
||||
issueIds={relation.issueIds}
|
||||
disabled={disabled}
|
||||
issueOperations={issueOperations}
|
||||
handleIssueCrudState={handleIssueCrudState}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
|
|
@ -146,24 +148,44 @@ export const RelationsCollapsibleContent: FC<Props> = 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 && (
|
||||
<CreateUpdateIssueModal
|
||||
isOpen={issueCrudState?.update?.toggle}
|
||||
onClose={() => {
|
||||
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 ? (
|
||||
<CreateUpdateEpicModal
|
||||
isOpen={issueCrudState?.update?.toggle}
|
||||
onClose={() => {
|
||||
handleIssueCrudState("update", null, null);
|
||||
toggleCreateIssueModal(false);
|
||||
}}
|
||||
data={issueCrudState?.update?.issue ?? undefined}
|
||||
onSubmit={async (_issue: TIssue) => {
|
||||
await epicOperations.update(workspaceSlug, projectId, _issue.id, _issue);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CreateUpdateIssueModal
|
||||
isOpen={issueCrudState?.update?.toggle}
|
||||
onClose={() => {
|
||||
handleIssueCrudState("update", null, null);
|
||||
toggleCreateIssueModal(false);
|
||||
}}
|
||||
data={issueCrudState?.update?.issue ?? undefined}
|
||||
onSubmit={async (_issue: TIssue) => {
|
||||
await issueOperations.update(workspaceSlug, projectId, _issue.id, _issue);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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`,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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<Props> = observer((props) => {
|
|||
relationKey,
|
||||
relationIssueId,
|
||||
disabled = false,
|
||||
issueOperations,
|
||||
handleIssueCrudState,
|
||||
issueServiceType = EIssueServiceType.ISSUES,
|
||||
} = props;
|
||||
|
|
@ -57,6 +55,7 @@ export const RelationIssueListItem: FC<Props> = 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<Props> = observer((props) => {
|
|||
<span className="w-full truncate text-sm text-custom-text-100">{issue.name}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{!issue.is_epic && (
|
||||
<>
|
||||
<div
|
||||
className="flex-shrink-0 text-sm"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<RelationIssueProperty
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={relationIssueId}
|
||||
disabled={disabled}
|
||||
issueOperations={issueOperations}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-shrink-0 text-sm">
|
||||
<CustomMenu placement="bottom-end" ellipsis>
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleEditIssue}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Pencil className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Edit issue</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<div
|
||||
className="flex-shrink-0 text-sm"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<RelationIssueProperty
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={relationIssueId}
|
||||
disabled={disabled}
|
||||
issueOperations={issueOperations}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-shrink-0 text-sm">
|
||||
<CustomMenu placement="bottom-end" ellipsis>
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleEditIssue}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Pencil className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Edit</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
||||
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Copy issue link</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Copy link</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleRemoveRelation}>
|
||||
<div className="flex items-center gap-2">
|
||||
<X className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Remove relation</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleRemoveRelation}>
|
||||
<div className="flex items-center gap-2">
|
||||
<X className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Remove relation</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleDeleteIssue}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Trash className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Delete issue</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!disabled && (
|
||||
<CustomMenu.MenuItem onClick={handleDeleteIssue}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Trash className="h-3.5 w-3.5" strokeWidth={2} />
|
||||
<span>Delete</span>
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ControlLink>
|
||||
|
|
|
|||
|
|
@ -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<Props> = observer((props) => {
|
|||
issueIds,
|
||||
relationKey,
|
||||
disabled = false,
|
||||
issueOperations,
|
||||
handleIssueCrudState,
|
||||
issueServiceType = EIssueServiceType.ISSUES,
|
||||
} = props;
|
||||
|
|
@ -50,7 +46,6 @@ export const RelationIssueList: FC<Props> = observer((props) => {
|
|||
relationIssueId={relationIssueId}
|
||||
disabled={disabled}
|
||||
handleIssueCrudState={handleIssueCrudState}
|
||||
issueOperations={issueOperations}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue