mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: Handled the draft issue from issue create modal and optimised the draft issue store (#3588)
Co-authored-by: gurusainath <gurusainath007@gmail.com>
This commit is contained in:
parent
0a35fcfbc0
commit
729b6ac79e
@ -1668,15 +1668,9 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return (
|
return (
|
||||||
Issue.objects.annotate(
|
Issue.objects.filter(
|
||||||
sub_issues_count=Issue.issue_objects.filter(
|
project_id=self.kwargs.get("project_id")
|
||||||
parent=OuterRef("id")
|
|
||||||
)
|
|
||||||
.order_by()
|
|
||||||
.annotate(count=Func(F("id"), function="Count"))
|
|
||||||
.values("count")
|
|
||||||
)
|
)
|
||||||
.filter(project_id=self.kwargs.get("project_id"))
|
|
||||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||||
.filter(is_draft=True)
|
.filter(is_draft=True)
|
||||||
.select_related("workspace", "project", "state", "parent")
|
.select_related("workspace", "project", "state", "parent")
|
||||||
@ -1710,7 +1704,7 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
.annotate(count=Func(F("id"), function="Count"))
|
.annotate(count=Func(F("id"), function="Count"))
|
||||||
.values("count")
|
.values("count")
|
||||||
)
|
)
|
||||||
)
|
).distinct()
|
||||||
|
|
||||||
@method_decorator(gzip_page)
|
@method_decorator(gzip_page)
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
@ -1832,7 +1826,10 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
notification=True,
|
notification=True,
|
||||||
origin=request.META.get("HTTP_ORIGIN"),
|
origin=request.META.get("HTTP_ORIGIN"),
|
||||||
)
|
)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
issue = (
|
||||||
|
self.get_queryset().filter(pk=serializer.data["id"]).first()
|
||||||
|
)
|
||||||
|
return Response(IssueSerializer(issue).data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
@ -1868,10 +1865,13 @@ class IssueDraftViewSet(BaseViewSet):
|
|||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def retrieve(self, request, slug, project_id, pk=None):
|
def retrieve(self, request, slug, project_id, pk=None):
|
||||||
issue = Issue.objects.get(
|
issue = self.get_queryset().filter(pk=pk).first()
|
||||||
workspace__slug=slug, project_id=project_id, pk=pk, is_draft=True
|
return Response(
|
||||||
|
IssueSerializer(
|
||||||
|
issue, fields=self.fields, expand=self.expand
|
||||||
|
).data,
|
||||||
|
status=status.HTTP_200_OK,
|
||||||
)
|
)
|
||||||
return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
def destroy(self, request, slug, project_id, pk=None):
|
def destroy(self, request, slug, project_id, pk=None):
|
||||||
issue = Issue.objects.get(
|
issue = Issue.objects.get(
|
||||||
|
@ -163,6 +163,8 @@ export const CommandPalette: FC = observer(() => {
|
|||||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [handleKeyDown]);
|
}, [handleKeyDown]);
|
||||||
|
|
||||||
|
const isDraftIssue = router?.asPath?.includes("draft-issues") || false;
|
||||||
|
|
||||||
if (!currentUser) return null;
|
if (!currentUser) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -217,6 +219,7 @@ export const CommandPalette: FC = observer(() => {
|
|||||||
onClose={() => toggleCreateIssueModal(false)}
|
onClose={() => toggleCreateIssueModal(false)}
|
||||||
data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined}
|
data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined}
|
||||||
storeType={createIssueStoreType}
|
storeType={createIssueStoreType}
|
||||||
|
isDraft={isDraftIssue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{workspaceSlug && projectId && issueId && issueDetails && (
|
{workspaceSlug && projectId && issueId && issueDetails && (
|
||||||
|
@ -103,7 +103,7 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
|||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
link={
|
link={
|
||||||
<BreadcrumbLink label="Inbox Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />
|
<BreadcrumbLink label="Draft Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
|
@ -66,16 +66,22 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
|||||||
</div>
|
</div>
|
||||||
</WithDisplayPropertiesHOC>
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
<ControlLink
|
{issue?.is_draft ? (
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
|
|
||||||
target="_blank"
|
|
||||||
onClick={() => handleIssuePeekOverview(issue)}
|
|
||||||
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100"
|
|
||||||
>
|
|
||||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
<span>{issue.name}</span>
|
<span>{issue.name}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ControlLink>
|
) : (
|
||||||
|
<ControlLink
|
||||||
|
href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
|
||||||
|
target="_blank"
|
||||||
|
onClick={() => handleIssuePeekOverview(issue)}
|
||||||
|
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100"
|
||||||
|
>
|
||||||
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
|
<span>{issue.name}</span>
|
||||||
|
</Tooltip>
|
||||||
|
</ControlLink>
|
||||||
|
)}
|
||||||
|
|
||||||
<IssueProperties
|
<IssueProperties
|
||||||
className="flex flex-wrap items-center gap-2 whitespace-nowrap"
|
className="flex flex-wrap items-center gap-2 whitespace-nowrap"
|
||||||
|
@ -79,21 +79,14 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isDraftIssue ? (
|
<CreateUpdateIssueModal
|
||||||
<CreateUpdateDraftIssueModal
|
isOpen={isOpen}
|
||||||
isOpen={isOpen}
|
onClose={() => setIsOpen(false)}
|
||||||
handleClose={() => setIsOpen(false)}
|
data={issuePayload}
|
||||||
prePopulateData={issuePayload}
|
storeType={storeType}
|
||||||
fieldsToShow={["all"]}
|
isDraft={isDraftIssue}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<CreateUpdateIssueModal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={() => setIsOpen(false)}
|
|
||||||
data={issuePayload}
|
|
||||||
storeType={storeType}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{renderExistingIssueModal && (
|
{renderExistingIssueModal && (
|
||||||
<ExistingIssuesListModal
|
<ExistingIssuesListModal
|
||||||
workspaceSlug={workspaceSlug?.toString()}
|
workspaceSlug={workspaceSlug?.toString()}
|
||||||
|
@ -69,16 +69,22 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
|||||||
<div className="absolute left-0 top-0 z-[99999] h-full w-full animate-pulse bg-custom-background-100/20" />
|
<div className="absolute left-0 top-0 z-[99999] h-full w-full animate-pulse bg-custom-background-100/20" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ControlLink
|
{issue?.is_draft ? (
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${issueId}`}
|
|
||||||
target="_blank"
|
|
||||||
onClick={() => handleIssuePeekOverview(issue)}
|
|
||||||
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100"
|
|
||||||
>
|
|
||||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
<span>{issue.name}</span>
|
<span>{issue.name}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ControlLink>
|
) : (
|
||||||
|
<ControlLink
|
||||||
|
href={`/${workspaceSlug}/projects/${projectId}/issues/${issueId}`}
|
||||||
|
target="_blank"
|
||||||
|
onClick={() => handleIssuePeekOverview(issue)}
|
||||||
|
className="w-full line-clamp-1 cursor-pointer text-sm text-custom-text-100"
|
||||||
|
>
|
||||||
|
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||||
|
<span>{issue.name}</span>
|
||||||
|
</Tooltip>
|
||||||
|
</ControlLink>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="ml-auto flex flex-shrink-0 items-center gap-2">
|
<div className="ml-auto flex flex-shrink-0 items-center gap-2">
|
||||||
{!issue?.tempId ? (
|
{!issue?.tempId ? (
|
||||||
|
@ -109,21 +109,13 @@ export const HeaderGroupByCard = observer(
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{isDraftIssue ? (
|
<CreateUpdateIssueModal
|
||||||
<CreateUpdateDraftIssueModal
|
isOpen={isOpen}
|
||||||
isOpen={isOpen}
|
onClose={() => setIsOpen(false)}
|
||||||
handleClose={() => setIsOpen(false)}
|
data={issuePayload}
|
||||||
prePopulateData={issuePayload}
|
storeType={storeType}
|
||||||
fieldsToShow={["all"]}
|
isDraft={isDraftIssue}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<CreateUpdateIssueModal
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={() => setIsOpen(false)}
|
|
||||||
data={issuePayload}
|
|
||||||
storeType={storeType}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{renderExistingIssueModal && (
|
{renderExistingIssueModal && (
|
||||||
<ExistingIssuesListModal
|
<ExistingIssuesListModal
|
||||||
|
@ -54,6 +54,8 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) =>
|
|||||||
};
|
};
|
||||||
delete duplicateIssuePayload.id;
|
delete duplicateIssuePayload.id;
|
||||||
|
|
||||||
|
const isDraftIssue = router?.asPath?.includes("draft-issues") || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteIssueModal
|
<DeleteIssueModal
|
||||||
@ -62,6 +64,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) =>
|
|||||||
handleClose={() => setDeleteIssueModal(false)}
|
handleClose={() => setDeleteIssueModal(false)}
|
||||||
onSubmit={handleDelete}
|
onSubmit={handleDelete}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CreateUpdateIssueModal
|
<CreateUpdateIssueModal
|
||||||
isOpen={createUpdateIssueModal}
|
isOpen={createUpdateIssueModal}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
@ -73,7 +76,9 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) =>
|
|||||||
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.PROJECT}
|
storeType={EIssuesStoreType.PROJECT}
|
||||||
|
isDraft={isDraftIssue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
customButton={customActionButton}
|
customButton={customActionButton}
|
||||||
|
@ -21,6 +21,7 @@ export interface DraftIssueProps {
|
|||||||
onClose: (saveDraftIssueInLocalStorage?: boolean) => void;
|
onClose: (saveDraftIssueInLocalStorage?: boolean) => void;
|
||||||
onSubmit: (formData: Partial<TIssue>) => Promise<void>;
|
onSubmit: (formData: Partial<TIssue>) => Promise<void>;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
isDraft: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const issueDraftService = new IssueDraftService();
|
const issueDraftService = new IssueDraftService();
|
||||||
@ -35,6 +36,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
|
|||||||
projectId,
|
projectId,
|
||||||
isCreateMoreToggleEnabled,
|
isCreateMoreToggleEnabled,
|
||||||
onCreateMoreToggleChange,
|
onCreateMoreToggleChange,
|
||||||
|
isDraft,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [issueDiscardModal, setIssueDiscardModal] = useState(false);
|
const [issueDiscardModal, setIssueDiscardModal] = useState(false);
|
||||||
@ -107,6 +109,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
|
|||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
|
isDraft={isDraft}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FC, useState, useRef, useEffect } from "react";
|
import React, { FC, useState, useRef, useEffect, Fragment } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
@ -55,8 +55,9 @@ export interface IssueFormProps {
|
|||||||
onCreateMoreToggleChange: (value: boolean) => void;
|
onCreateMoreToggleChange: (value: boolean) => void;
|
||||||
onChange?: (formData: Partial<TIssue> | null) => void;
|
onChange?: (formData: Partial<TIssue> | null) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit: (values: Partial<TIssue>) => Promise<void>;
|
onSubmit: (values: Partial<TIssue>, is_draft_issue?: boolean) => Promise<void>;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
isDraft: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// services
|
// services
|
||||||
@ -72,6 +73,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
projectId: defaultProjectId,
|
projectId: defaultProjectId,
|
||||||
isCreateMoreToggleEnabled,
|
isCreateMoreToggleEnabled,
|
||||||
onCreateMoreToggleChange,
|
onCreateMoreToggleChange,
|
||||||
|
isDraft,
|
||||||
} = props;
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [labelModal, setLabelModal] = useState(false);
|
const [labelModal, setLabelModal] = useState(false);
|
||||||
@ -137,8 +139,8 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
|
|
||||||
const issueName = watch("name");
|
const issueName = watch("name");
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<TIssue>) => {
|
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue = false) => {
|
||||||
await onSubmit(formData);
|
await onSubmit(formData, is_draft_issue);
|
||||||
|
|
||||||
setGptAssistantModal(false);
|
setGptAssistantModal(false);
|
||||||
|
|
||||||
@ -248,7 +250,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
<form>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
{/* Don't show project selection if editing an issue */}
|
{/* Don't show project selection if editing an issue */}
|
||||||
@ -670,7 +672,40 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
<Button variant="neutral-primary" size="sm" onClick={onClose} tabIndex={17}>
|
<Button variant="neutral-primary" size="sm" onClick={onClose} tabIndex={17}>
|
||||||
Discard
|
Discard
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" variant="primary" size="sm" loading={isSubmitting} tabIndex={18}>
|
|
||||||
|
{isDraft && (
|
||||||
|
<Fragment>
|
||||||
|
{data?.id ? (
|
||||||
|
<Button
|
||||||
|
variant="neutral-primary"
|
||||||
|
size="sm"
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={handleSubmit((data) => handleFormSubmit({ ...data, is_draft: false }))}
|
||||||
|
tabIndex={18}
|
||||||
|
>
|
||||||
|
{isSubmitting ? "Moving" : "Move from draft"}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="neutral-primary"
|
||||||
|
size="sm"
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={handleSubmit((data) => handleFormSubmit(data, true))}
|
||||||
|
tabIndex={18}
|
||||||
|
>
|
||||||
|
{isSubmitting ? "Saving" : "Save as draft"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
loading={isSubmitting}
|
||||||
|
tabIndex={isDraft ? 19 : 18}
|
||||||
|
onClick={handleSubmit((data) => handleFormSubmit(data))}
|
||||||
|
>
|
||||||
{data?.id ? (isSubmitting ? "Updating" : "Update issue") : isSubmitting ? "Creating" : "Create issue"}
|
{data?.id ? (isSubmitting ? "Updating" : "Update issue") : isSubmitting ? "Creating" : "Create issue"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,10 +20,19 @@ export interface IssuesModalProps {
|
|||||||
onSubmit?: (res: TIssue) => Promise<void>;
|
onSubmit?: (res: TIssue) => Promise<void>;
|
||||||
withDraftIssueWrapper?: boolean;
|
withDraftIssueWrapper?: boolean;
|
||||||
storeType?: TCreateModalStoreTypes;
|
storeType?: TCreateModalStoreTypes;
|
||||||
|
isDraft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((props) => {
|
export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((props) => {
|
||||||
const { data, isOpen, onClose, onSubmit, withDraftIssueWrapper = true, storeType = EIssuesStoreType.PROJECT } = props;
|
const {
|
||||||
|
data,
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
withDraftIssueWrapper = true,
|
||||||
|
storeType = EIssuesStoreType.PROJECT,
|
||||||
|
isDraft = false,
|
||||||
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [changesMade, setChangesMade] = useState<Partial<TIssue> | null>(null);
|
const [changesMade, setChangesMade] = useState<Partial<TIssue> | null>(null);
|
||||||
const [createMore, setCreateMore] = useState(false);
|
const [createMore, setCreateMore] = useState(false);
|
||||||
@ -42,6 +51,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
const { issues: cycleIssues } = useIssues(EIssuesStoreType.CYCLE);
|
const { issues: cycleIssues } = useIssues(EIssuesStoreType.CYCLE);
|
||||||
const { issues: viewIssues } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
const { issues: viewIssues } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||||
const { issues: profileIssues } = useIssues(EIssuesStoreType.PROFILE);
|
const { issues: profileIssues } = useIssues(EIssuesStoreType.PROFILE);
|
||||||
|
const { issues: draftIssueStore } = useIssues(EIssuesStoreType.DRAFT);
|
||||||
// store mapping based on current store
|
// store mapping based on current store
|
||||||
const issueStores = {
|
const issueStores = {
|
||||||
[EIssuesStoreType.PROJECT]: {
|
[EIssuesStoreType.PROJECT]: {
|
||||||
@ -122,11 +132,16 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateIssue = async (payload: Partial<TIssue>): Promise<TIssue | undefined> => {
|
const handleCreateIssue = async (
|
||||||
|
payload: Partial<TIssue>,
|
||||||
|
is_draft_issue: boolean = false
|
||||||
|
): Promise<TIssue | undefined> => {
|
||||||
if (!workspaceSlug || !payload.project_id) return;
|
if (!workspaceSlug || !payload.project_id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await currentIssueStore.createIssue(workspaceSlug, payload.project_id, payload, viewId);
|
const response = is_draft_issue
|
||||||
|
? await draftIssueStore.createIssue(workspaceSlug, payload.project_id, payload)
|
||||||
|
: await currentIssueStore.createIssue(workspaceSlug, payload.project_id, payload, viewId);
|
||||||
if (!response) throw new Error();
|
if (!response) throw new Error();
|
||||||
|
|
||||||
currentIssueStore.fetchIssues(workspaceSlug, payload.project_id, "mutation", viewId);
|
currentIssueStore.fetchIssues(workspaceSlug, payload.project_id, "mutation", viewId);
|
||||||
@ -213,7 +228,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<TIssue>) => {
|
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue: boolean = false) => {
|
||||||
if (!workspaceSlug || !formData.project_id || !storeType) return;
|
if (!workspaceSlug || !formData.project_id || !storeType) return;
|
||||||
|
|
||||||
const payload: Partial<TIssue> = {
|
const payload: Partial<TIssue> = {
|
||||||
@ -222,7 +237,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
};
|
};
|
||||||
|
|
||||||
let response: TIssue | undefined = undefined;
|
let response: TIssue | undefined = undefined;
|
||||||
if (!data?.id) response = await handleCreateIssue(payload);
|
if (!data?.id) response = await handleCreateIssue(payload, is_draft_issue);
|
||||||
else response = await handleUpdateIssue(payload);
|
else response = await handleUpdateIssue(payload);
|
||||||
|
|
||||||
if (response != undefined && onSubmit) await onSubmit(response);
|
if (response != undefined && onSubmit) await onSubmit(response);
|
||||||
@ -274,6 +289,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
projectId={activeProjectId}
|
projectId={activeProjectId}
|
||||||
isCreateMoreToggleEnabled={createMore}
|
isCreateMoreToggleEnabled={createMore}
|
||||||
onCreateMoreToggleChange={handleCreateMoreToggleChange}
|
onCreateMoreToggleChange={handleCreateMoreToggleChange}
|
||||||
|
isDraft={isDraft}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IssueFormRoot
|
<IssueFormRoot
|
||||||
@ -287,6 +303,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
onCreateMoreToggleChange={handleCreateMoreToggleChange}
|
onCreateMoreToggleChange={handleCreateMoreToggleChange}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
projectId={activeProjectId}
|
projectId={activeProjectId}
|
||||||
|
isDraft={isDraft}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
|
import update from "lodash/update";
|
||||||
|
import uniq from "lodash/uniq";
|
||||||
|
import concat from "lodash/concat";
|
||||||
|
import pull from "lodash/pull";
|
||||||
// base class
|
// base class
|
||||||
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
import { IssueHelperStore } from "../helpers/issue-helper.store";
|
||||||
// services
|
// services
|
||||||
@ -123,7 +127,7 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||||||
const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data);
|
const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[projectId].push(response.id);
|
update(this.issues, [projectId], (issueIds = []) => uniq(concat(issueIds, response.id)));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootStore.issues.addIssue([response]);
|
this.rootStore.issues.addIssue([response]);
|
||||||
@ -136,8 +140,17 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||||||
|
|
||||||
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||||
try {
|
try {
|
||||||
this.rootStore.issues.updateIssue(issueId, data);
|
const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||||
const response = await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
|
||||||
|
if (data.hasOwnProperty("is_draft") && data?.is_draft === false) {
|
||||||
|
runInAction(() => {
|
||||||
|
update(this.issues, [projectId], (issueIds = []) => {
|
||||||
|
if (issueIds.includes(issueId)) pull(issueIds, issueId);
|
||||||
|
return issueIds;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||||
@ -147,15 +160,14 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues {
|
|||||||
|
|
||||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await this.issueDraftService.deleteDraftIssue(workspaceSlug, projectId, issueId);
|
const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const issueIndex = this.issues[projectId].findIndex((_issueId) => _issueId === issueId);
|
runInAction(() => {
|
||||||
if (issueIndex >= 0)
|
update(this.issues, [projectId], (issueIds = []) => {
|
||||||
runInAction(() => {
|
if (issueIds.includes(issueId)) pull(issueIds, issueId);
|
||||||
this.issues[projectId].splice(issueIndex, 1);
|
return issueIds;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
this.rootStore.issues.removeIssue(issueId);
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user